From 86a9eee047ba09a714c3b8e27c9df2bbf715393a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo <acme@redhat.com> Date: Mon, 14 Dec 2009 20:09:31 -0200 Subject: perf diff: Introduce tool to show performance difference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I guess it is enough to show some examples: [root@doppio linux-2.6-tip]# rm -f perf.data* [root@doppio linux-2.6-tip]# ls -la perf.data* ls: cannot access perf.data*: No such file or directory [root@doppio linux-2.6-tip]# perf record -f find / > /dev/null [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.062 MB perf.data (~2699 samples) ] [root@doppio linux-2.6-tip]# ls -la perf.data* -rw------- 1 root root 74440 2009-12-14 20:03 perf.data [root@doppio linux-2.6-tip]# perf record -f find / > /dev/null [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.062 MB perf.data (~2692 samples) ] [root@doppio linux-2.6-tip]# ls -la perf.data* -rw------- 1 root root 74280 2009-12-14 20:03 perf.data -rw------- 1 root root 74440 2009-12-14 20:03 perf.data.old [root@doppio linux-2.6-tip]# perf diff | head -5 1 -34994580 /lib64/libc-2.10.1.so _IO_vfprintf_internal 2 -15307806 [kernel.kallsyms] __kmalloc 3 +1 +3665941 /lib64/libc-2.10.1.so __GI_memmove 4 +4 +23508995 /lib64/libc-2.10.1.so _int_malloc 5 +7 +38538813 [kernel.kallsyms] __d_lookup [root@doppio linux-2.6-tip]# perf diff -p | head -5 1 +1.00% /lib64/libc-2.10.1.so _IO_vfprintf_internal 2 [kernel.kallsyms] __kmalloc 3 +1 /lib64/libc-2.10.1.so __GI_memmove 4 +4 /lib64/libc-2.10.1.so _int_malloc 5 +7 -1.00% [kernel.kallsyms] __d_lookup [root@doppio linux-2.6-tip]# perf diff -v | head -5 1 361449551 326454971 -34994580 /lib64/libc-2.10.1.so _IO_vfprintf_internal 2 151009241 135701435 -15307806 [kernel.kallsyms] __kmalloc 3 +1 101805328 105471269 +3665941 /lib64/libc-2.10.1.so __GI_memmove 4 +4 78041440 101550435 +23508995 /lib64/libc-2.10.1.so _int_malloc 5 +7 59536172 98074985 +38538813 [kernel.kallsyms] __d_lookup [root@doppio linux-2.6-tip]# perf diff -vp | head -5 1 9.00% 8.00% +1.00% /lib64/libc-2.10.1.so _IO_vfprintf_internal 2 3.00% 3.00% [kernel.kallsyms] __kmalloc 3 +1 2.00% 2.00% /lib64/libc-2.10.1.so __GI_memmove 4 +4 2.00% 2.00% /lib64/libc-2.10.1.so _int_malloc 5 +7 1.00% 2.00% -1.00% [kernel.kallsyms] __d_lookup [root@doppio linux-2.6-tip]# This should be enough for diffs where the system is non volatile, i.e. when one doesn't updates binaries. For volatile environments, stay tuned for the next perf tool feature: a buildid cache populated by 'perf record', managed by 'perf buildid-cache' a-la ccache, and used by all the report tools. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: Stephen Hemminger <shemminger@vyatta.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> LKML-Reference: <1260828571-3613-3-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- tools/perf/builtin-diff.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 tools/perf/builtin-diff.c (limited to 'tools/perf/builtin-diff.c') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c new file mode 100644 index 00000000000..0d528018ffb --- /dev/null +++ b/tools/perf/builtin-diff.c @@ -0,0 +1,288 @@ +/* + * builtin-diff.c + * + * Builtin diff command: Analyze two perf.data input files, look up and read + * DSOs and symbol information, sort them and produce a diff. + */ +#include "builtin.h" + +#include "util/debug.h" +#include "util/event.h" +#include "util/hist.h" +#include "util/session.h" +#include "util/sort.h" +#include "util/symbol.h" +#include "util/util.h" + +#include <stdlib.h> + +static char const *input_old = "perf.data.old", + *input_new = "perf.data"; +static int force; +static bool show_percent; + +struct symbol_conf symbol_conf; + +static int perf_session__add_hist_entry(struct perf_session *self, + struct addr_location *al, u64 count) +{ + bool hit; + struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL, + count, &hit); + if (he == NULL) + return -ENOMEM; + + if (hit) + he->count += count; + + return 0; +} + +static int diff__process_sample_event(event_t *event, struct perf_session *session) +{ + struct addr_location al; + struct sample_data data = { .period = 1, }; + + dump_printf("(IP, %d): %d: %p\n", event->header.misc, + event->ip.pid, (void *)(long)event->ip.ip); + + if (event__preprocess_sample(event, session, &al, NULL) < 0) { + pr_warning("problem processing %d event, skipping it.\n", + event->header.type); + return -1; + } + + event__parse_sample(event, session->sample_type, &data); + + if (al.sym && perf_session__add_hist_entry(session, &al, data.period)) { + pr_warning("problem incrementing symbol count, skipping event\n"); + return -1; + } + + session->events_stats.total += data.period; + return 0; +} + +static struct perf_event_ops event_ops = { + .process_sample_event = diff__process_sample_event, + .process_mmap_event = event__process_mmap, + .process_comm_event = event__process_comm, + .process_exit_event = event__process_task, + .process_fork_event = event__process_task, + .process_lost_event = event__process_lost, +}; + +static void perf_session__insert_hist_entry_by_name(struct rb_root *root, + struct hist_entry *he) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct hist_entry *iter; + + while (*p != NULL) { + int cmp; + parent = *p; + iter = rb_entry(parent, struct hist_entry, rb_node); + + cmp = strcmp(he->map->dso->name, iter->map->dso->name); + if (cmp > 0) + p = &(*p)->rb_left; + else if (cmp < 0) + p = &(*p)->rb_right; + else { + cmp = strcmp(he->sym->name, iter->sym->name); + if (cmp > 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&he->rb_node, parent, p); + rb_insert_color(&he->rb_node, root); +} + +static void perf_session__resort_by_name(struct perf_session *self) +{ + unsigned long position = 1; + struct rb_root tmp = RB_ROOT; + struct rb_node *next = rb_first(&self->hists); + + while (next != NULL) { + struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); + + next = rb_next(&n->rb_node); + rb_erase(&n->rb_node, &self->hists); + n->position = position++; + perf_session__insert_hist_entry_by_name(&tmp, n); + } + + self->hists = tmp; +} + +static struct hist_entry * +perf_session__find_hist_entry_by_name(struct perf_session *self, + struct hist_entry *he) +{ + struct rb_node *n = self->hists.rb_node; + + while (n) { + struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); + int cmp = strcmp(he->map->dso->name, iter->map->dso->name); + + if (cmp > 0) + n = n->rb_left; + else if (cmp < 0) + n = n->rb_right; + else { + cmp = strcmp(he->sym->name, iter->sym->name); + if (cmp > 0) + n = n->rb_left; + else if (cmp < 0) + n = n->rb_right; + else + return iter; + } + } + + return NULL; +} + +static void perf_session__match_hists(struct perf_session *old_session, + struct perf_session *new_session) +{ + struct rb_node *nd; + + perf_session__resort_by_name(old_session); + + for (nd = rb_first(&new_session->hists); nd; nd = rb_next(nd)) { + struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); + pos->pair = perf_session__find_hist_entry_by_name(old_session, pos); + } +} + +static size_t hist_entry__fprintf_matched(struct hist_entry *self, + unsigned long pos, + struct perf_session *session, + struct perf_session *pair_session, + FILE *fp) +{ + u64 old_count = 0; + char displacement[16]; + size_t printed; + + if (self->pair != NULL) { + long pdiff = (long)self->pair->position - (long)pos; + old_count = self->pair->count; + if (pdiff == 0) + goto blank; + snprintf(displacement, sizeof(displacement), "%+4ld", pdiff); + } else { +blank: memset(displacement, ' ', sizeof(displacement)); + } + + printed = fprintf(fp, "%4lu %5.5s ", pos, displacement); + + if (show_percent) { + double old_percent = (old_count * 100) / pair_session->events_stats.total, + new_percent = (self->count * 100) / session->events_stats.total; + double diff = old_percent - new_percent; + + if (verbose) + printed += fprintf(fp, " %3.2f%% %3.2f%%", old_percent, new_percent); + + if ((u64)diff != 0) + printed += fprintf(fp, " %+4.2F%%", diff); + else + printed += fprintf(fp, " "); + } else { + if (verbose) + printed += fprintf(fp, " %9Lu %9Lu", old_count, self->count); + printed += fprintf(fp, " %+9Ld", (s64)self->count - (s64)old_count); + } + + return printed + fprintf(fp, " %25.25s %s\n", + self->map->dso->name, self->sym->name); +} + +static size_t perf_session__fprintf_matched_hists(struct perf_session *self, + struct perf_session *pair, + FILE *fp) +{ + struct rb_node *nd; + size_t printed = 0; + unsigned long pos = 1; + + for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { + struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); + printed += hist_entry__fprintf_matched(he, pos++, self, pair, fp); + } + + return printed; +} + +static int __cmd_diff(void) +{ + int ret, i; + struct perf_session *session[2]; + + session[0] = perf_session__new(input_old, O_RDONLY, force, &symbol_conf); + session[1] = perf_session__new(input_new, O_RDONLY, force, &symbol_conf); + if (session[0] == NULL || session[1] == NULL) + return -ENOMEM; + + for (i = 0; i < 2; ++i) { + ret = perf_session__process_events(session[i], &event_ops); + if (ret) + goto out_delete; + perf_session__output_resort(session[i], session[i]->events_stats.total); + } + + perf_session__match_hists(session[0], session[1]); + perf_session__fprintf_matched_hists(session[1], session[0], stdout); +out_delete: + for (i = 0; i < 2; ++i) + perf_session__delete(session[i]); + return ret; +} + +static const char *const diff_usage[] = { + "perf diff [<options>] [old_file] [new_file]", +}; + +static const struct option options[] = { + OPT_BOOLEAN('v', "verbose", &verbose, + "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, + "dump raw trace in ASCII"), + OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), + OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, + "load module symbols - WARNING: use only with -k and LIVE kernel"), + OPT_BOOLEAN('p', "percentages", &show_percent, + "Don't shorten the pathnames taking into account the cwd"), + OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths, + "Don't shorten the pathnames taking into account the cwd"), + OPT_END() +}; + +int cmd_diff(int argc, const char **argv, const char *prefix __used) +{ + if (symbol__init(&symbol_conf) < 0) + return -1; + + setup_sorting(diff_usage, options); + + argc = parse_options(argc, argv, options, diff_usage, 0); + if (argc) { + if (argc > 2) + usage_with_options(diff_usage, options); + if (argc == 2) { + input_old = argv[0]; + input_new = argv[1]; + } else + input_new = argv[0]; + } + + setup_pager(); + return __cmd_diff(); +} -- cgit v1.2.3-70-g09d2 From 75be6cf48738aec68aac49b428423569492cfba3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo <acme@redhat.com> Date: Tue, 15 Dec 2009 20:04:39 -0200 Subject: perf symbols: Make symbol_conf global MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies a lot of functions, less stuff to be done by tool writers. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1260914682-29652-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- tools/perf/builtin-annotate.c | 14 ++++++-------- tools/perf/builtin-buildid-list.c | 5 +++-- tools/perf/builtin-diff.c | 8 +++----- tools/perf/builtin-kmem.c | 5 ++--- tools/perf/builtin-probe.c | 14 ++++++-------- tools/perf/builtin-record.c | 4 ++-- tools/perf/builtin-report.c | 7 ++----- tools/perf/builtin-sched.c | 5 ++--- tools/perf/builtin-timechart.c | 5 ++--- tools/perf/builtin-top.c | 6 ++---- tools/perf/builtin-trace.c | 4 ++-- tools/perf/util/session.c | 5 ++--- tools/perf/util/session.h | 5 +---- tools/perf/util/symbol.c | 38 +++++++++++++++----------------------- tools/perf/util/symbol.h | 9 ++++----- 15 files changed, 54 insertions(+), 80 deletions(-) (limited to 'tools/perf/builtin-diff.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 2e2855a685c..e656e25f1c1 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -51,11 +51,6 @@ struct sym_priv { struct sym_ext *ext; }; -static struct symbol_conf symbol_conf = { - .priv_size = sizeof(struct sym_priv), - .try_vmlinux_path = true, -}; - static const char *sym_hist_filter; static int symbol_filter(struct map *map __used, struct symbol *sym) @@ -464,10 +459,10 @@ static struct perf_event_ops event_ops = { static int __cmd_annotate(void) { - struct perf_session *session = perf_session__new(input_name, O_RDONLY, - force, &symbol_conf); int ret; + struct perf_session *session; + session = perf_session__new(input_name, O_RDONLY, force); if (session == NULL) return -ENOMEM; @@ -523,7 +518,10 @@ static const struct option options[] = { int cmd_annotate(int argc, const char **argv, const char *prefix __used) { - if (symbol__init(&symbol_conf) < 0) + symbol_conf.priv_size = sizeof(struct sym_priv); + symbol_conf.try_vmlinux_path = true; + + if (symbol__init() < 0) return -1; argc = parse_options(argc, argv, options, annotate_usage, 0); diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 7c36e4b2ecc..e693e6777af 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -54,8 +54,9 @@ static int perf_file_section__process_buildids(struct perf_file_section *self, static int __cmd_buildid_list(void) { int err = -1; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, - force, NULL); + struct perf_session *session; + + session = perf_session__new(input_name, O_RDONLY, force); if (session == NULL) return -1; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 0d528018ffb..67328d10699 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -21,8 +21,6 @@ static char const *input_old = "perf.data.old", static int force; static bool show_percent; -struct symbol_conf symbol_conf; - static int perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, u64 count) { @@ -226,8 +224,8 @@ static int __cmd_diff(void) int ret, i; struct perf_session *session[2]; - session[0] = perf_session__new(input_old, O_RDONLY, force, &symbol_conf); - session[1] = perf_session__new(input_new, O_RDONLY, force, &symbol_conf); + session[0] = perf_session__new(input_old, O_RDONLY, force); + session[1] = perf_session__new(input_new, O_RDONLY, force); if (session[0] == NULL || session[1] == NULL) return -ENOMEM; @@ -267,7 +265,7 @@ static const struct option options[] = { int cmd_diff(int argc, const char **argv, const char *prefix __used) { - if (symbol__init(&symbol_conf) < 0) + if (symbol__init() < 0) return -1; setup_sorting(diff_usage, options); diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index dda60869faa..e078797ab1f 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -505,8 +505,7 @@ static void sort_result(void) static int __cmd_kmem(void) { int err; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, - 0, NULL); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); if (session == NULL) return -ENOMEM; @@ -767,7 +766,7 @@ static int __cmd_record(int argc, const char **argv) int cmd_kmem(int argc, const char **argv, const char *prefix __used) { - symbol__init(0); + symbol__init(); argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 520b064b46d..7e741f54d79 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -57,7 +57,6 @@ static struct { int nr_probe; struct probe_point probes[MAX_PROBES]; struct strlist *dellist; - struct symbol_conf conf; struct perf_session *psession; struct map *kmap; } session; @@ -151,7 +150,7 @@ static const struct option options[] = { OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show parsed arguments, etc)"), #ifndef NO_LIBDWARF - OPT_STRING('k', "vmlinux", &session.conf.vmlinux_name, + OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), #endif OPT_BOOLEAN('l', "list", &session.list_events, @@ -224,13 +223,12 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) } /* Initialize symbol maps for vmlinux */ - session.conf.sort_by_name = true; - if (session.conf.vmlinux_name == NULL) - session.conf.try_vmlinux_path = true; - if (symbol__init(&session.conf) < 0) + symbol_conf.sort_by_name = true; + if (symbol_conf.vmlinux_name == NULL) + symbol_conf.try_vmlinux_path = true; + if (symbol__init() < 0) die("Failed to init symbol map."); - session.psession = perf_session__new(NULL, O_WRONLY, false, - &session.conf); + session.psession = perf_session__new(NULL, O_WRONLY, false); if (session.psession == NULL) die("Failed to init perf_session."); session.kmap = map_groups__find_by_name(&session.psession->kmaps, diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 66979a5553b..1da48a8fe9c 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -451,7 +451,7 @@ static int __cmd_record(int argc, const char **argv) exit(-1); } - session = perf_session__new(output_name, O_WRONLY, force, NULL); + session = perf_session__new(output_name, O_WRONLY, force); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); return -1; @@ -632,7 +632,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) { int counter; - symbol__init(0); + symbol__init(); argc = parse_options(argc, argv, options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 40389c0e38c..c349bdbb947 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -52,9 +52,6 @@ static int exclude_other = 1; static char callchain_default_opt[] = "fractal,0.5"; -static struct symbol_conf symbol_conf; - - static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) { @@ -705,7 +702,7 @@ static int __cmd_report(void) int ret; struct perf_session *session; - session = perf_session__new(input_name, O_RDONLY, force, &symbol_conf); + session = perf_session__new(input_name, O_RDONLY, force); if (session == NULL) return -ENOMEM; @@ -864,7 +861,7 @@ static void setup_list(struct strlist **list, const char *list_str, int cmd_report(int argc, const char **argv, const char *prefix __used) { - if (symbol__init(&symbol_conf) < 0) + if (symbol__init() < 0) return -1; argc = parse_options(argc, argv, options, report_usage, 0); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d67f274adba..80209df6cfe 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1675,8 +1675,7 @@ static struct perf_event_ops event_ops = { static int read_events(void) { int err; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, - 0, NULL); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); if (session == NULL) return -ENOMEM; @@ -1912,7 +1911,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used) if (!strcmp(argv[0], "trace")) return cmd_trace(argc, argv, prefix); - symbol__init(0); + symbol__init(); if (!strncmp(argv[0], "rec", 3)) { return __cmd_record(argc, argv); } else if (!strncmp(argv[0], "lat", 3)) { diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index ffd81e87ce6..9c98b7a2b19 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -1050,8 +1050,7 @@ static struct perf_event_ops event_ops = { static int __cmd_timechart(void) { - struct perf_session *session = perf_session__new(input_name, O_RDONLY, - 0, NULL); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); int ret; if (session == NULL) @@ -1138,7 +1137,7 @@ static const struct option options[] = { int cmd_timechart(int argc, const char **argv, const char *prefix __used) { - symbol__init(0); + symbol__init(); argc = parse_options(argc, argv, options, timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 296e809c253..cd89b6d036b 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -80,7 +80,6 @@ static int dump_symtab = 0; static bool hide_kernel_symbols = false; static bool hide_user_symbols = false; static struct winsize winsize; -static struct symbol_conf symbol_conf; /* * Source @@ -1162,8 +1161,7 @@ static int __cmd_top(void) * FIXME: perf_session__new should allow passing a O_MMAP, so that all this * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. */ - struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, - &symbol_conf); + struct perf_session *session = perf_session__new(NULL, O_WRONLY, false); if (session == NULL) return -ENOMEM; @@ -1284,7 +1282,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) (nr_counters + 1) * sizeof(unsigned long)); if (symbol_conf.vmlinux_name == NULL) symbol_conf.try_vmlinux_path = true; - if (symbol__init(&symbol_conf) < 0) + if (symbol__init() < 0) return -1; if (delay_secs < 1) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7e744f77404..07ad25ca654 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -579,7 +579,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) exit(-1); } - symbol__init(0); + symbol__init(); setup_scripting(); @@ -588,7 +588,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) setup_pager(); - session = perf_session__new(input_name, O_RDONLY, 0, NULL); + session = perf_session__new(input_name, O_RDONLY, 0); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ecd54bedfb1..bceaa09f55a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -49,8 +49,7 @@ out_close: return -1; } -struct perf_session *perf_session__new(const char *filename, int mode, - bool force, struct symbol_conf *conf) +struct perf_session *perf_session__new(const char *filename, int mode, bool force) { size_t len = filename ? strlen(filename) + 1 : 0; struct perf_session *self = zalloc(sizeof(*self) + len); @@ -69,7 +68,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, self->cwdlen = 0; map_groups__init(&self->kmaps); - if (perf_session__create_kernel_maps(self, conf) < 0) + if (perf_session__create_kernel_maps(self) < 0) goto out_delete; if (mode == O_RDONLY && perf_session__open(self, force) < 0) diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index bdfc4b8eee7..faf18a8e031 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -10,7 +10,6 @@ struct ip_callchain; struct thread; struct symbol; -struct symbol_conf; struct perf_session { struct perf_header header; @@ -26,7 +25,6 @@ struct perf_session { int fd; int cwdlen; char *cwd; - bool use_modules; bool use_callchain; char filename[0]; }; @@ -48,8 +46,7 @@ struct perf_event_ops { bool full_paths; }; -struct perf_session *perf_session__new(const char *filename, int mode, - bool force, struct symbol_conf *conf); +struct perf_session *perf_session__new(const char *filename, int mode, bool force); void perf_session__delete(struct perf_session *self); int perf_session__process_events(struct perf_session *self, diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 185b9eec192..17ce01269a9 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -33,11 +33,10 @@ static void dsos__add(struct list_head *head, struct dso *dso); static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); static int dso__load_kernel_sym(struct dso *self, struct map *map, struct perf_session *session, symbol_filter_t filter); -unsigned int symbol__priv_size; static int vmlinux_path__nr_entries; static char **vmlinux_path; -static struct symbol_conf symbol_conf__defaults = { +struct symbol_conf symbol_conf = { .use_modules = true, .try_vmlinux_path = true, }; @@ -130,13 +129,13 @@ static void map_groups__fixup_end(struct map_groups *self) static struct symbol *symbol__new(u64 start, u64 len, const char *name) { size_t namelen = strlen(name) + 1; - struct symbol *self = zalloc(symbol__priv_size + + struct symbol *self = zalloc(symbol_conf.priv_size + sizeof(*self) + namelen); if (self == NULL) return NULL; - if (symbol__priv_size) - self = ((void *)self) + symbol__priv_size; + if (symbol_conf.priv_size) + self = ((void *)self) + symbol_conf.priv_size; self->start = start; self->end = len ? start + len - 1 : start; @@ -150,7 +149,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) static void symbol__delete(struct symbol *self) { - free(((void *)self) - symbol__priv_size); + free(((void *)self) - symbol_conf.priv_size); } static size_t symbol__fprintf(struct symbol *self, FILE *fp) @@ -471,7 +470,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, module = strchr(pos->name, '\t'); if (module) { - if (!session->use_modules) + if (!symbol_conf.use_modules) goto discard_symbol; *module++ = '\0'; @@ -1740,34 +1739,27 @@ out_fail: return -1; } -int symbol__init(struct symbol_conf *conf) +int symbol__init(void) { - const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults; - elf_version(EV_CURRENT); - symbol__priv_size = pconf->priv_size; - if (pconf->sort_by_name) - symbol__priv_size += (sizeof(struct symbol_name_rb_node) - - sizeof(struct symbol)); + if (symbol_conf.sort_by_name) + symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - + sizeof(struct symbol)); - if (pconf->try_vmlinux_path && vmlinux_path__init() < 0) + if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) return -1; return 0; } -int perf_session__create_kernel_maps(struct perf_session *self, - struct symbol_conf *conf) +int perf_session__create_kernel_maps(struct perf_session *self) { - const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults; - if (map_groups__create_kernel_maps(&self->kmaps, - pconf->vmlinux_name) < 0) + symbol_conf.vmlinux_name) < 0) return -1; - self->use_modules = pconf->use_modules; - - if (pconf->use_modules && perf_session__create_module_maps(self) < 0) + if (symbol_conf.use_modules && + perf_session__create_module_maps(self) < 0) pr_debug("Failed to load list of modules for session %s, " "continuing...\n", self->filename); /* diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 941ef331790..766294735f9 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -57,11 +57,11 @@ struct symbol_conf { const char *vmlinux_name; }; -extern unsigned int symbol__priv_size; +extern struct symbol_conf symbol_conf; static inline void *symbol__priv(struct symbol *self) { - return ((void *)self) - symbol__priv_size; + return ((void *)self) - symbol_conf.priv_size; } struct addr_location { @@ -119,9 +119,8 @@ int sysfs__read_build_id(const char *filename, void *bf, size_t size); bool dsos__read_build_ids(void); int build_id__sprintf(u8 *self, int len, char *bf); -int symbol__init(struct symbol_conf *conf); -int perf_session__create_kernel_maps(struct perf_session *self, - struct symbol_conf *conf); +int symbol__init(void); +int perf_session__create_kernel_maps(struct perf_session *self); extern struct list_head dsos__user, dsos__kernel; extern struct dso *vdso; -- cgit v1.2.3-70-g09d2 From 655000e7c75a559681ee7f15f6fa870c80ae3194 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo <acme@redhat.com> Date: Tue, 15 Dec 2009 20:04:40 -0200 Subject: perf symbols: Adopt the strlists for dso, comm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will be used in perf diff too. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1260914682-29652-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- tools/perf/builtin-annotate.c | 4 +-- tools/perf/builtin-diff.c | 9 +++--- tools/perf/builtin-kmem.c | 4 +-- tools/perf/builtin-record.c | 6 ++-- tools/perf/builtin-report.c | 73 ++++++++++++++++++------------------------ tools/perf/builtin-timechart.c | 4 +-- tools/perf/builtin-trace.c | 4 +-- tools/perf/util/symbol.c | 33 +++++++++++++++++++ tools/perf/util/symbol.h | 9 ++++++ 9 files changed, 89 insertions(+), 57 deletions(-) (limited to 'tools/perf/builtin-diff.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index e656e25f1c1..645d5805843 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -518,14 +518,14 @@ static const struct option options[] = { int cmd_annotate(int argc, const char **argv, const char *prefix __used) { + argc = parse_options(argc, argv, options, annotate_usage, 0); + symbol_conf.priv_size = sizeof(struct sym_priv); symbol_conf.try_vmlinux_path = true; if (symbol__init() < 0) return -1; - argc = parse_options(argc, argv, options, annotate_usage, 0); - setup_sorting(annotate_usage, options); if (argc) { diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 67328d10699..4fde6065534 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -265,11 +265,6 @@ static const struct option options[] = { int cmd_diff(int argc, const char **argv, const char *prefix __used) { - if (symbol__init() < 0) - return -1; - - setup_sorting(diff_usage, options); - argc = parse_options(argc, argv, options, diff_usage, 0); if (argc) { if (argc > 2) @@ -281,6 +276,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used) input_new = argv[0]; } + if (symbol__init() < 0) + return -1; + + setup_sorting(diff_usage, options); setup_pager(); return __cmd_diff(); } diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index e078797ab1f..fc21ad79dd8 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -766,13 +766,13 @@ static int __cmd_record(int argc, const char **argv) int cmd_kmem(int argc, const char **argv, const char *prefix __used) { - symbol__init(); - argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); if (!argc) usage_with_options(kmem_usage, kmem_options); + symbol__init(); + if (!strncmp(argv[0], "rec", 3)) { return __cmd_record(argc, argv); } else if (!strcmp(argv[0], "stat")) { diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 1da48a8fe9c..65301c5ca1d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -632,13 +632,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) { int counter; - symbol__init(); - argc = parse_options(argc, argv, options, record_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + PARSE_OPT_STOP_AT_NON_OPTION); if (!argc && target_pid == -1 && !system_wide) usage_with_options(record_usage, options); + symbol__init(); + if (!nr_counters) { nr_counters = 1; attrs[0].type = PERF_TYPE_HARDWARE; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c349bdbb947..03afac3b56e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -33,10 +33,6 @@ static char const *input_name = "perf.data"; -static char *dso_list_str, *comm_list_str, *sym_list_str, - *col_width_list_str; -static struct strlist *dso_list, *comm_list, *sym_list; - static int force; static bool use_callchain; @@ -365,8 +361,9 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, static void dso__calc_col_width(struct dso *self) { - if (!col_width_list_str && !field_sep && - (!dso_list || strlist__has_entry(dso_list, self->name))) { + if (!symbol_conf.col_width_list_str && !field_sep && + (!symbol_conf.dso_list || + strlist__has_entry(symbol_conf.dso_list, self->name))) { unsigned int slen = strlen(self->name); if (slen > dsos__col_width) dsos__col_width = slen; @@ -379,8 +376,9 @@ static void thread__comm_adjust(struct thread *self) { char *comm = self->comm; - if (!col_width_list_str && !field_sep && - (!comm_list || strlist__has_entry(comm_list, comm))) { + if (!symbol_conf.col_width_list_str && !field_sep && + (!symbol_conf.comm_list || + strlist__has_entry(symbol_conf.comm_list, comm))) { unsigned int slen = strlen(comm); if (slen > comms__col_width) { @@ -442,7 +440,7 @@ static size_t perf_session__fprintf_hist_entries(struct perf_session *self, struct rb_node *nd; size_t ret = 0; unsigned int width; - char *col_width = col_width_list_str; + char *col_width = symbol_conf.col_width_list_str; int raw_printing_style; raw_printing_style = !strcmp(pretty_printing_style, "raw"); @@ -468,7 +466,7 @@ static size_t perf_session__fprintf_hist_entries(struct perf_session *self, } width = strlen(se->header); if (se->width) { - if (col_width_list_str) { + if (symbol_conf.col_width_list_str) { if (col_width) { *se->width = atoi(col_width); col_width = strchr(col_width, ','); @@ -587,7 +585,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - if (comm_list && !strlist__has_entry(comm_list, thread->comm)) + if (symbol_conf.comm_list && + !strlist__has_entry(symbol_conf.comm_list, thread->comm)) return 0; cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -601,14 +600,15 @@ static int process_sample_event(event_t *event, struct perf_session *session) if (al.map && !sort_dso.elide && !al.map->dso->slen_calculated) dso__calc_col_width(al.map->dso); - if (dso_list && + if (symbol_conf.dso_list && (!al.map || !al.map->dso || - !(strlist__has_entry(dso_list, al.map->dso->short_name) || + !(strlist__has_entry(symbol_conf.dso_list, al.map->dso->short_name) || (al.map->dso->short_name != al.map->dso->long_name && - strlist__has_entry(dso_list, al.map->dso->long_name))))) + strlist__has_entry(symbol_conf.dso_list, al.map->dso->long_name))))) return 0; - if (sym_list && al.sym && !strlist__has_entry(sym_list, al.sym->name)) + if (symbol_conf.sym_list && al.sym && + !strlist__has_entry(symbol_conf.sym_list, al.sym->name)) return 0; if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) { @@ -825,13 +825,13 @@ static const struct option options[] = { OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", "Display callchains using output_type and min percent threshold. " "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), - OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", + OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", "only consider symbols in these dsos"), - OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", + OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", "only consider symbols in these comms"), - OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", + OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", "only consider these symbols"), - OPT_STRING('w', "column-widths", &col_width_list_str, + OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, "width[,width...]", "don't try to adjust column width, use these fixed values"), OPT_STRING('t', "field-separator", &field_sep, "separator", @@ -840,32 +840,25 @@ static const struct option options[] = { OPT_END() }; -static void setup_list(struct strlist **list, const char *list_str, - struct sort_entry *se, const char *list_name, - FILE *fp) +static void sort_entry__setup_elide(struct sort_entry *self, + struct strlist *list, + const char *list_name, FILE *fp) { - if (list_str) { - *list = strlist__new(true, list_str); - if (!*list) { - fprintf(stderr, "problems parsing %s list\n", - list_name); - exit(129); - } - if (strlist__nr_entries(*list) == 1) { - fprintf(fp, "# %s: %s\n", list_name, - strlist__entry(*list, 0)->s); - se->elide = true; - } + if (list && strlist__nr_entries(list) == 1) { + fprintf(fp, "# %s: %s\n", list_name, strlist__entry(list, 0)->s); + self->elide = true; } } int cmd_report(int argc, const char **argv, const char *prefix __used) { + argc = parse_options(argc, argv, options, report_usage, 0); + + setup_pager(); + if (symbol__init() < 0) return -1; - argc = parse_options(argc, argv, options, report_usage, 0); - setup_sorting(report_usage, options); if (parent_pattern != default_parent_pattern) { @@ -880,11 +873,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) if (argc) usage_with_options(report_usage, options); - setup_pager(); - - setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); - setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); - setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); + sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); + sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); if (field_sep && *field_sep == '.') { fputs("'.' is the only non valid --field-separator argument\n", diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 9c98b7a2b19..a589a43112d 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -1137,11 +1137,11 @@ static const struct option options[] = { int cmd_timechart(int argc, const char **argv, const char *prefix __used) { - symbol__init(); - argc = parse_options(argc, argv, options, timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION); + symbol__init(); + if (argc && !strncmp(argv[0], "rec", 3)) return __cmd_record(argc, argv); else if (argc) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 07ad25ca654..e2285e28720 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -579,13 +579,13 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) exit(-1); } - symbol__init(); - setup_scripting(); argc = parse_options(argc, argv, options, annotate_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (symbol__init() < 0) + return -1; setup_pager(); session = perf_session__new(input_name, O_RDONLY, 0); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 17ce01269a9..164286ace7d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1,6 +1,7 @@ #include "util.h" #include "../perf.h" #include "session.h" +#include "sort.h" #include "string.h" #include "symbol.h" #include "thread.h" @@ -1739,6 +1740,20 @@ out_fail: return -1; } +static int setup_list(struct strlist **list, const char *list_str, + const char *list_name) +{ + if (list_str == NULL) + return 0; + + *list = strlist__new(true, list_str); + if (!*list) { + pr_err("problems parsing %s list\n", list_name); + return -1; + } + return 0; +} + int symbol__init(void) { elf_version(EV_CURRENT); @@ -1749,7 +1764,25 @@ int symbol__init(void) if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) return -1; + if (setup_list(&symbol_conf.dso_list, + symbol_conf.dso_list_str, "dso") < 0) + return -1; + + if (setup_list(&symbol_conf.comm_list, + symbol_conf.comm_list_str, "comm") < 0) + goto out_free_dso_list; + + if (setup_list(&symbol_conf.sym_list, + symbol_conf.sym_list_str, "symbol") < 0) + goto out_free_comm_list; + return 0; + +out_free_dso_list: + strlist__delete(symbol_conf.dso_list); +out_free_comm_list: + strlist__delete(symbol_conf.comm_list); + return -1; } int perf_session__create_kernel_maps(struct perf_session *self) diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 766294735f9..d61f3507499 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -49,12 +49,21 @@ struct symbol { char name[0]; }; +struct strlist; + struct symbol_conf { unsigned short priv_size; bool try_vmlinux_path, use_modules, sort_by_name; const char *vmlinux_name; + char *dso_list_str, + *comm_list_str, + *sym_list_str, + *col_width_list_str; + struct strlist *dso_list, + *comm_list, + *sym_list; }; extern struct symbol_conf symbol_conf; -- cgit v1.2.3-70-g09d2 From c410a33887c17cac95ed8b0d860cdfb5c087a7d8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo <acme@redhat.com> Date: Tue, 15 Dec 2009 20:04:41 -0200 Subject: perf symbols: Move symbol filtering to event__preprocess_sample() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that --dsos, --comm, --symbols can bem used in more tools, like in perf diff: $ perf record -f find / > /dev/null $ perf record -f find / > /dev/null $ perf diff --dsos /lib64/libc-2.10.1.so | head -5 1 +22392124 /lib64/libc-2.10.1.so _IO_vfprintf_internal 2 +6410655 /lib64/libc-2.10.1.so __GI_memmove 3 +1 +9192692 /lib64/libc-2.10.1.so _int_malloc 4 -1 -15158605 /lib64/libc-2.10.1.so _int_free 5 +45669 /lib64/libc-2.10.1.so _IO_new_file_xsputn $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1260914682-29652-3-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-diff.c | 19 ++++++++-- tools/perf/builtin-report.c | 86 ++++++++----------------------------------- tools/perf/builtin-top.c | 2 +- tools/perf/util/event.c | 42 +++++++++++++++++++++ tools/perf/util/symbol.c | 5 +++ tools/perf/util/symbol.h | 4 +- 7 files changed, 84 insertions(+), 76 deletions(-) (limited to 'tools/perf/builtin-diff.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 645d5805843..593ff25006d 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -141,7 +141,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) return -1; } - if (perf_session__add_hist_entry(session, &al, 1)) { + if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) { fprintf(stderr, "problem incrementing symbol count, " "skipping event\n"); return -1; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 4fde6065534..ff91e9c291b 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -50,6 +50,9 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi return -1; } + if (al.filtered) + return 0; + event__parse_sample(event, session->sample_type, &data); if (al.sym && perf_session__add_hist_entry(session, &al, data.period)) { @@ -182,10 +185,14 @@ blank: memset(displacement, ' ', sizeof(displacement)); printed = fprintf(fp, "%4lu %5.5s ", pos, displacement); if (show_percent) { - double old_percent = (old_count * 100) / pair_session->events_stats.total, - new_percent = (self->count * 100) / session->events_stats.total; - double diff = old_percent - new_percent; + double old_percent = 0, new_percent = 0, diff; + + if (pair_session->events_stats.total > 0) + old_percent = (old_count * 100) / pair_session->events_stats.total; + if (session->events_stats.total > 0) + new_percent = (self->count * 100) / session->events_stats.total; + diff = old_percent - new_percent; if (verbose) printed += fprintf(fp, " %3.2f%% %3.2f%%", old_percent, new_percent); @@ -260,6 +267,12 @@ static const struct option options[] = { "Don't shorten the pathnames taking into account the cwd"), OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths, "Don't shorten the pathnames taking into account the cwd"), + OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", + "only consider symbols in these dsos"), + OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", + "only consider symbols in these comms"), + OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", + "only consider these symbols"), OPT_END() }; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 03afac3b56e..9c595340326 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -316,14 +316,14 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, if (total_samples) ret = percent_color_fprintf(fp, - field_sep ? "%.2f" : " %6.2f%%", + symbol_conf.field_sep ? "%.2f" : " %6.2f%%", (self->count * 100.0) / total_samples); else - ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); + ret = fprintf(fp, symbol_conf.field_sep ? "%lld" : "%12lld ", self->count); if (show_nr_samples) { - if (field_sep) - fprintf(fp, "%c%lld", *field_sep, self->count); + if (symbol_conf.field_sep) + fprintf(fp, "%c%lld", *symbol_conf.field_sep, self->count); else fprintf(fp, "%11lld", self->count); } @@ -332,7 +332,7 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, if (se->elide) continue; - fprintf(fp, "%s", field_sep ?: " "); + fprintf(fp, "%s", symbol_conf.field_sep ?: " "); ret += se->print(fp, self, se->width ? *se->width : 0); } @@ -355,28 +355,11 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, return ret; } -/* - * - */ - -static void dso__calc_col_width(struct dso *self) -{ - if (!symbol_conf.col_width_list_str && !field_sep && - (!symbol_conf.dso_list || - strlist__has_entry(symbol_conf.dso_list, self->name))) { - unsigned int slen = strlen(self->name); - if (slen > dsos__col_width) - dsos__col_width = slen; - } - - self->slen_calculated = 1; -} - static void thread__comm_adjust(struct thread *self) { char *comm = self->comm; - if (!symbol_conf.col_width_list_str && !field_sep && + if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.comm_list || strlist__has_entry(symbol_conf.comm_list, comm))) { unsigned int slen = strlen(comm); @@ -452,16 +435,16 @@ static size_t perf_session__fprintf_hist_entries(struct perf_session *self, fprintf(fp, "# Overhead"); if (show_nr_samples) { - if (field_sep) - fprintf(fp, "%cSamples", *field_sep); + if (symbol_conf.field_sep) + fprintf(fp, "%cSamples", *symbol_conf.field_sep); else fputs(" Samples ", fp); } list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) continue; - if (field_sep) { - fprintf(fp, "%c%s", *field_sep, se->header); + if (symbol_conf.field_sep) { + fprintf(fp, "%c%s", *symbol_conf.field_sep, se->header); continue; } width = strlen(se->header); @@ -480,7 +463,7 @@ static size_t perf_session__fprintf_hist_entries(struct perf_session *self, } fprintf(fp, "\n"); - if (field_sep) + if (symbol_conf.field_sep) goto print_entries; fprintf(fp, "# ........"); @@ -542,13 +525,8 @@ static int validate_chain(struct ip_callchain *chain, event_t *event) static int process_sample_event(event_t *event, struct perf_session *session) { - struct sample_data data; - int cpumode; + struct sample_data data = { .period = 1, }; struct addr_location al; - struct thread *thread; - - memset(&data, 0, sizeof(data)); - data.period = 1; event__parse_sample(event, session->sample_type, &data); @@ -576,39 +554,13 @@ static int process_sample_event(event_t *event, struct perf_session *session) } } - thread = perf_session__findnew(session, data.pid); - if (thread == NULL) { - pr_debug("problem processing %d event, skipping it.\n", + if (event__preprocess_sample(event, session, &al, NULL) < 0) { + fprintf(stderr, "problem processing %d event, skipping it.\n", event->header.type); return -1; } - dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - - if (symbol_conf.comm_list && - !strlist__has_entry(symbol_conf.comm_list, thread->comm)) - return 0; - - cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - - thread__find_addr_location(thread, session, cpumode, - MAP__FUNCTION, data.ip, &al, NULL); - /* - * We have to do this here as we may have a dso with no symbol hit that - * has a name longer than the ones with symbols sampled. - */ - if (al.map && !sort_dso.elide && !al.map->dso->slen_calculated) - dso__calc_col_width(al.map->dso); - - if (symbol_conf.dso_list && - (!al.map || !al.map->dso || - !(strlist__has_entry(symbol_conf.dso_list, al.map->dso->short_name) || - (al.map->dso->short_name != al.map->dso->long_name && - strlist__has_entry(symbol_conf.dso_list, al.map->dso->long_name))))) - return 0; - - if (symbol_conf.sym_list && al.sym && - !strlist__has_entry(symbol_conf.sym_list, al.sym->name)) + if (al.filtered) return 0; if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) { @@ -834,7 +786,7 @@ static const struct option options[] = { OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, "width[,width...]", "don't try to adjust column width, use these fixed values"), - OPT_STRING('t', "field-separator", &field_sep, "separator", + OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", "separator for columns, no spaces will be added between " "columns '.' is reserved."), OPT_END() @@ -877,11 +829,5 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); - if (field_sep && *field_sep == '.') { - fputs("'.' is the only non valid --field-separator argument\n", - stderr); - exit(129); - } - return __cmd_report(); } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index cd89b6d036b..ddc584b6487 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -948,7 +948,7 @@ static void event__process_sample(const event_t *self, } if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || - al.sym == NULL) + al.sym == NULL || al.filtered) return; syme = symbol__priv(al.sym); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 375fb6dca1c..bf491fda1f4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -2,7 +2,9 @@ #include "event.h" #include "debug.h" #include "session.h" +#include "sort.h" #include "string.h" +#include "strlist.h" #include "thread.h" static pid_t event__synthesize_comm(pid_t pid, int full, @@ -299,6 +301,19 @@ try_again: } } +static void dso__calc_col_width(struct dso *self) +{ + if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && + (!symbol_conf.dso_list || + strlist__has_entry(symbol_conf.dso_list, self->name))) { + unsigned int slen = strlen(self->name); + if (slen > dsos__col_width) + dsos__col_width = slen; + } + + self->slen_calculated = 1; +} + int event__preprocess_sample(const event_t *self, struct perf_session *session, struct addr_location *al, symbol_filter_t filter) { @@ -308,6 +323,10 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, if (thread == NULL) return -1; + if (symbol_conf.comm_list && + !strlist__has_entry(symbol_conf.comm_list, thread->comm)) + goto out_filtered; + dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, @@ -315,6 +334,29 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, dump_printf(" ...... dso: %s\n", al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : "<not found>"); + /* + * We have to do this here as we may have a dso with no symbol hit that + * has a name longer than the ones with symbols sampled. + */ + if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) + dso__calc_col_width(al->map->dso); + + if (symbol_conf.dso_list && + (!al->map || !al->map->dso || + !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || + (al->map->dso->short_name != al->map->dso->long_name && + strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) + goto out_filtered; + + if (symbol_conf.sym_list && al->sym && + !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) + goto out_filtered; + + al->filtered = false; + return 0; + +out_filtered: + al->filtered = true; return 0; } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 164286ace7d..7707897b59f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1764,6 +1764,11 @@ int symbol__init(void) if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) return -1; + if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { + pr_err("'.' is the only non valid --field-separator argument\n"); + return -1; + } + if (setup_list(&symbol_conf.dso_list, symbol_conf.dso_list_str, "dso") < 0) return -1; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d61f3507499..60151521f41 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -56,7 +56,8 @@ struct symbol_conf { bool try_vmlinux_path, use_modules, sort_by_name; - const char *vmlinux_name; + const char *vmlinux_name, + *field_sep; char *dso_list_str, *comm_list_str, *sym_list_str, @@ -79,6 +80,7 @@ struct addr_location { struct symbol *sym; u64 addr; char level; + bool filtered; }; struct dso { -- cgit v1.2.3-70-g09d2 From c351c2816177eb7d2979ec874b9b895abe9d6e5c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo <acme@redhat.com> Date: Wed, 16 Dec 2009 13:49:27 -0200 Subject: perf diff: Use perf_session__fprintf_hists just like 'perf record' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That means that almost everything you can do with 'perf report' can be done with 'perf diff', for instance: $ perf record -f find / > /dev/null [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.062 MB perf.data (~2699 samples) ] $ perf record -f find / > /dev/null [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.062 MB perf.data (~2687 samples) ] perf diff | head -8 9.02% +1.00% find libc-2.10.1.so [.] _IO_vfprintf_internal 2.91% -1.00% find [kernel] [k] __kmalloc 2.85% -1.00% find [kernel] [k] ext4_htree_store_dirent 1.99% -1.00% find [kernel] [k] _atomic_dec_and_lock 2.44% find [kernel] [k] half_md4_transform $ So if you want to zoom into libc: $ perf diff --dsos libc-2.10.1.so | head -8 37.34% find [.] _IO_vfprintf_internal 10.34% find [.] __GI_memmove 8.25% +2.00% find [.] _int_malloc 5.07% -1.00% find [.] __GI_mempcpy 7.62% +2.00% find [.] _int_free $ And if there were multiple commands using libc, it is also possible to aggregate them all by using --sort symbol: $ perf diff --dsos libc-2.10.1.so --sort symbol | head -8 37.34% [.] _IO_vfprintf_internal 10.34% [.] __GI_memmove 8.25% +2.00% [.] _int_malloc 5.07% -1.00% [.] __GI_mempcpy 7.62% +2.00% [.] _int_free $ The displacement column now is off by default, to use it: perf diff -m --dsos libc-2.10.1.so --sort symbol | head -8 37.34% [.] _IO_vfprintf_internal 10.34% [.] __GI_memmove 8.25% +2.00% [.] _int_malloc 5.07% -1.00% +2 [.] __GI_mempcpy 7.62% +2.00% -1 [.] _int_free $ Using -t/--field-separator can be used for scripting: $ perf diff -t, -m --dsos libc-2.10.1.so --sort symbol | head -8 37.34, , ,[.] _IO_vfprintf_internal 10.34, , ,[.] __GI_memmove 8.25,+2.00%, ,[.] _int_malloc 5.07,-1.00%, +2,[.] __GI_mempcpy 7.62,+2.00%, -1,[.] _int_free 6.99,+1.00%, -1,[.] _IO_new_file_xsputn 1.89,-2.00%, +4,[.] __readdir64 $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1260978567-550-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- tools/perf/Documentation/perf-diff.txt | 29 ++++++- tools/perf/Documentation/perf-report.txt | 4 + tools/perf/builtin-diff.c | 90 +++++----------------- tools/perf/builtin-report.c | 12 +-- tools/perf/util/hist.c | 126 +++++++++++++++++++++++++------ tools/perf/util/hist.h | 5 +- tools/perf/util/sort.c | 11 +++ tools/perf/util/sort.h | 2 + 8 files changed, 169 insertions(+), 110 deletions(-) (limited to 'tools/perf/builtin-diff.c') diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 6936fdb6d47..8974e208cba 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt @@ -19,9 +19,32 @@ If no parameters are passed it will assume perf.data.old and perf.data. OPTIONS ------- --p:: ---percentage:: - Show percentages instead of raw counts +-d:: +--dsos=:: + Only consider symbols in these dsos. CSV that understands + file://filename entries. + +-C:: +--comms=:: + Only consider symbols in these comms. CSV that understands + file://filename entries. + +-S:: +--symbols=:: + Only consider these symbols. CSV that understands + file://filename entries. + +-s:: +--sort=:: + Sort by key(s): pid, comm, dso, symbol. + +-t:: +--field-separator=:: + + Use a special separator character and don't pad with spaces, replacing + all occurances of this separator in symbol names (and other output) + with a '.' character, that thus it's the only non valid separator. + -v:: --verbose:: Be verbose, for instance, show the raw counts in addition to the diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 9dccb180b7a..abfabe9147a 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -39,6 +39,10 @@ OPTIONS Only consider these symbols. CSV that understands file://filename entries. +-s:: +--sort=:: + Sort by key(s): pid, comm, dso, symbol, parent. + -w:: --field-width=:: Force each column width to the provided list, for large terminal diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index ff91e9c291b..66f100d249a 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -16,10 +16,10 @@ #include <stdlib.h> -static char const *input_old = "perf.data.old", - *input_new = "perf.data"; -static int force; -static bool show_percent; +static char const *input_old = "perf.data.old", + *input_new = "perf.data"; +static int force; +static bool show_displacement; static int perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, u64 count) @@ -162,70 +162,6 @@ static void perf_session__match_hists(struct perf_session *old_session, } } -static size_t hist_entry__fprintf_matched(struct hist_entry *self, - unsigned long pos, - struct perf_session *session, - struct perf_session *pair_session, - FILE *fp) -{ - u64 old_count = 0; - char displacement[16]; - size_t printed; - - if (self->pair != NULL) { - long pdiff = (long)self->pair->position - (long)pos; - old_count = self->pair->count; - if (pdiff == 0) - goto blank; - snprintf(displacement, sizeof(displacement), "%+4ld", pdiff); - } else { -blank: memset(displacement, ' ', sizeof(displacement)); - } - - printed = fprintf(fp, "%4lu %5.5s ", pos, displacement); - - if (show_percent) { - double old_percent = 0, new_percent = 0, diff; - - if (pair_session->events_stats.total > 0) - old_percent = (old_count * 100) / pair_session->events_stats.total; - if (session->events_stats.total > 0) - new_percent = (self->count * 100) / session->events_stats.total; - - diff = old_percent - new_percent; - if (verbose) - printed += fprintf(fp, " %3.2f%% %3.2f%%", old_percent, new_percent); - - if ((u64)diff != 0) - printed += fprintf(fp, " %+4.2F%%", diff); - else - printed += fprintf(fp, " "); - } else { - if (verbose) - printed += fprintf(fp, " %9Lu %9Lu", old_count, self->count); - printed += fprintf(fp, " %+9Ld", (s64)self->count - (s64)old_count); - } - - return printed + fprintf(fp, " %25.25s %s\n", - self->map->dso->name, self->sym->name); -} - -static size_t perf_session__fprintf_matched_hists(struct perf_session *self, - struct perf_session *pair, - FILE *fp) -{ - struct rb_node *nd; - size_t printed = 0; - unsigned long pos = 1; - - for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { - struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); - printed += hist_entry__fprintf_matched(he, pos++, self, pair, fp); - } - - return printed; -} - static int __cmd_diff(void) { int ret, i; @@ -244,7 +180,8 @@ static int __cmd_diff(void) } perf_session__match_hists(session[0], session[1]); - perf_session__fprintf_matched_hists(session[1], session[0], stdout); + perf_session__fprintf_hists(session[1], session[0], + show_displacement, stdout); out_delete: for (i = 0; i < 2; ++i) perf_session__delete(session[i]); @@ -258,13 +195,13 @@ static const char *const diff_usage[] = { static const struct option options[] = { OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('m', "displacement", &show_displacement, + "Show position displacement relative to baseline"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, "load module symbols - WARNING: use only with -k and LIVE kernel"), - OPT_BOOLEAN('p', "percentages", &show_percent, - "Don't shorten the pathnames taking into account the cwd"), OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths, "Don't shorten the pathnames taking into account the cwd"), OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", @@ -273,6 +210,11 @@ static const struct option options[] = { "only consider symbols in these comms"), OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", "only consider these symbols"), + OPT_STRING('s', "sort", &sort_order, "key[,key2...]", + "sort by key(s): pid, comm, dso, symbol, parent"), + OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", + "separator for columns, no spaces will be added between " + "columns '.' is reserved."), OPT_END() }; @@ -289,10 +231,16 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used) input_new = argv[0]; } + symbol_conf.exclude_other = false; if (symbol__init() < 0) return -1; setup_sorting(diff_usage, options); setup_pager(); + + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); + sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", NULL); + sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", NULL); + return __cmd_diff(); } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index cf1d370f51c..e50a6b10ee6 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -225,7 +225,7 @@ static int __cmd_report(void) perf_session__collapse_resort(session); perf_session__output_resort(session, session->events_stats.total); fprintf(stdout, "# Samples: %ld\n#\n", session->events_stats.total); - perf_session__fprintf_hists(session, stdout); + perf_session__fprintf_hists(session, NULL, false, stdout); if (sort_order == default_sort_order && parent_pattern == default_parent_pattern) fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); @@ -344,16 +344,6 @@ static const struct option options[] = { OPT_END() }; -static void sort_entry__setup_elide(struct sort_entry *self, - struct strlist *list, - const char *list_name, FILE *fp) -{ - if (list && strlist__nr_entries(list) == 1) { - fprintf(fp, "# %s: %s\n", list_name, strlist__entry(list, 0)->s); - self->elide = true; - } -} - int cmd_report(int argc, const char **argv, const char *prefix __used) { argc = parse_options(argc, argv, options, report_usage, 0); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 6e416a62e0d..ecf853cdc0b 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -454,34 +454,80 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, - struct perf_session *session) +static size_t hist_entry__fprintf(struct hist_entry *self, + struct perf_session *session, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp) { struct sort_entry *se; + u64 count, total; + const char *sep = symbol_conf.field_sep; size_t ret; if (symbol_conf.exclude_other && !self->parent) return 0; - if (session->events_stats.total) - ret = percent_color_fprintf(fp, - symbol_conf.field_sep ? "%.2f" : " %6.2f%%", - (self->count * 100.0) / session->events_stats.total); + if (pair_session) { + count = self->pair ? self->pair->count : 0; + total = pair_session->events_stats.total; + } else { + count = self->count; + total = session->events_stats.total; + } + + if (total) + ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); else - ret = fprintf(fp, symbol_conf.field_sep ? "%lld" : "%12lld ", self->count); + ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); if (symbol_conf.show_nr_samples) { - if (symbol_conf.field_sep) - fprintf(fp, "%c%lld", *symbol_conf.field_sep, self->count); + if (sep) + fprintf(fp, "%c%lld", *sep, count); else - fprintf(fp, "%11lld", self->count); + fprintf(fp, "%11lld", count); + } + + if (pair_session) { + char bf[32]; + double old_percent = 0, new_percent = 0, diff; + + if (total > 0) + old_percent = (count * 100) / total; + if (session->events_stats.total > 0) + new_percent = (self->count * 100) / session->events_stats.total; + + diff = old_percent - new_percent; + + if ((u64)diff != 0) + snprintf(bf, sizeof(bf), "%+4.2F%%", diff); + else + snprintf(bf, sizeof(bf), " "); + + if (sep) + ret += fprintf(fp, "%c%s", *sep, bf); + else + ret += fprintf(fp, "%11.11s", bf); + + if (show_displacement) { + if (displacement) + snprintf(bf, sizeof(bf), "%+4ld", displacement); + else + snprintf(bf, sizeof(bf), " "); + + if (sep) + fprintf(fp, "%c%s", *sep, bf); + else + fprintf(fp, "%6.6s", bf); + } } list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) continue; - fprintf(fp, "%s", symbol_conf.field_sep ?: " "); + fprintf(fp, "%s", sep ?: " "); ret += se->print(fp, self, se->width ? *se->width : 0); } @@ -504,29 +550,49 @@ static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, return ret; } -size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp) +size_t perf_session__fprintf_hists(struct perf_session *self, + struct perf_session *pair, + bool show_displacement, FILE *fp) { - struct hist_entry *pos; struct sort_entry *se; struct rb_node *nd; size_t ret = 0; + unsigned long position = 1; + long displacement = 0; unsigned int width; + const char *sep = symbol_conf.field_sep; char *col_width = symbol_conf.col_width_list_str; init_rem_hits(); - fprintf(fp, "# Overhead"); + fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); + if (symbol_conf.show_nr_samples) { - if (symbol_conf.field_sep) - fprintf(fp, "%cSamples", *symbol_conf.field_sep); + if (sep) + fprintf(fp, "%cSamples", *sep); else fputs(" Samples ", fp); } + + if (pair) { + if (sep) + ret += fprintf(fp, "%cDelta", *sep); + else + ret += fprintf(fp, " Delta "); + + if (show_displacement) { + if (sep) + ret += fprintf(fp, "%cDisplacement", *sep); + else + ret += fprintf(fp, " Displ"); + } + } + list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) continue; - if (symbol_conf.field_sep) { - fprintf(fp, "%c%s", *symbol_conf.field_sep, se->header); + if (sep) { + fprintf(fp, "%c%s", *sep, se->header); continue; } width = strlen(se->header); @@ -545,12 +611,17 @@ size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp) } fprintf(fp, "\n"); - if (symbol_conf.field_sep) + if (sep) goto print_entries; fprintf(fp, "# ........"); if (symbol_conf.show_nr_samples) fprintf(fp, " .........."); + if (pair) { + fprintf(fp, " .........."); + if (show_displacement) + fprintf(fp, " ....."); + } list_for_each_entry(se, &hist_entry__sort_list, list) { unsigned int i; @@ -565,14 +636,23 @@ size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp) for (i = 0; i < width; i++) fprintf(fp, "."); } - fprintf(fp, "\n"); - fprintf(fp, "#\n"); + fprintf(fp, "\n#\n"); print_entries: for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { - pos = rb_entry(nd, struct hist_entry, rb_node); - ret += hist_entry__fprintf(fp, pos, self); + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (show_displacement) { + if (h->pair != NULL) + displacement = ((long)h->pair->position - + (long)position); + else + displacement = 0; + ++position; + } + ret += hist_entry__fprintf(h, self, pair, show_displacement, + displacement, fp); } free(rem_sq_bracket); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index c7ac78d93b0..e5f99b24048 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -21,6 +21,7 @@ void hist_entry__free(struct hist_entry *); void perf_session__output_resort(struct perf_session *self, u64 total_samples); void perf_session__collapse_resort(struct perf_session *self); -size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp); - +size_t perf_session__fprintf_hists(struct perf_session *self, + struct perf_session *pair, + bool show_displacement, FILE *fp); #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cff1c316fa9..cb0f327de9e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -303,3 +303,14 @@ void setup_sorting(const char * const usagestr[], const struct option *opts) free(str); } + +void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, + const char *list_name, FILE *fp) +{ + if (list && strlist__nr_entries(list) == 1) { + if (fp != NULL) + fprintf(fp, "# %s: %s\n", list_name, + strlist__entry(list, 0)->s); + self->elide = true; + } +} diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 925f083e1ee..753f9ea99fb 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -101,5 +101,7 @@ extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); extern int sort_dimension__add(const char *); +void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, + const char *list_name, FILE *fp); #endif /* __PERF_SORT_H */ -- cgit v1.2.3-70-g09d2 From 604c5c92972dcb4b98be34775452d09a5d4ec248 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo <acme@redhat.com> Date: Wed, 16 Dec 2009 14:09:53 -0200 Subject: perf diff: Change the default sort order to "dso,symbol" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a more intuitive / more meaningful default: $ perf diff | head -8 9.02% +1.00% libc-2.10.1.so [.] _IO_vfprintf_internal 2.91% -1.00% [kernel] [k] __kmalloc 2.85% -1.00% [kernel] [k] ext4_htree_store_dirent 1.99% -1.00% [kernel] [k] _atomic_dec_and_lock 2.44% [kernel] $ Suggested-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1260979793-1981-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- tools/perf/builtin-diff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/builtin-diff.c') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 66f100d249a..4d33b55d558 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -18,6 +18,7 @@ static char const *input_old = "perf.data.old", *input_new = "perf.data"; +static char diff__default_sort_order[] = "dso,symbol"; static int force; static bool show_displacement; @@ -220,6 +221,7 @@ static const struct option options[] = { int cmd_diff(int argc, const char **argv, const char *prefix __used) { + sort_order = diff__default_sort_order; argc = parse_options(argc, argv, options, diff_usage, 0); if (argc) { if (argc > 2) -- cgit v1.2.3-70-g09d2 From 0422a4fc2c6a41c3d4184984797512af2650014e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo <acme@redhat.com> Date: Fri, 18 Dec 2009 16:35:58 -0200 Subject: perf diff: Fix usage array, it must end with a NULL entry Fixing this: [acme@doppio linux-2.6-tip]$ perf diff --hell Error: unknown option `hell' usage: perf diff [<options>] [old_file] [new_file] Segmentation fault [acme@doppio linux-2.6-tip]$ Also go over the other such arrays to check if they all were OK, they are, but there were some minor changes to do like making one static and renaming another to match the command it refers to. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1261161358-23959-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> --- tools/perf/builtin-buildid-list.c | 2 +- tools/perf/builtin-diff.c | 3 ++- tools/perf/builtin-report.c | 3 +-- tools/perf/builtin-trace.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tools/perf/builtin-diff.c') diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index e693e6777af..1e99ac80691 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -17,7 +17,7 @@ static char const *input_name = "perf.data"; static int force; -static const char *const buildid_list_usage[] = { +static const char * const buildid_list_usage[] = { "perf buildid-list [<options>]", NULL }; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 4d33b55d558..bd71b8ceafb 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -189,8 +189,9 @@ out_delete: return ret; } -static const char *const diff_usage[] = { +static const char * const diff_usage[] = { "perf diff [<options>] [old_file] [new_file]", + NULL, }; static const struct option options[] = { diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5c2ab5357ec..db10c0e8eca 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -294,8 +294,7 @@ setup: return 0; } -//static const char * const report_usage[] = { -const char * const report_usage[] = { +static const char * const report_usage[] = { "perf report [<options>] <command>", NULL }; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e2285e28720..574a215e800 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -512,7 +512,7 @@ static char *get_script_path(const char *script_root, const char *suffix) return path; } -static const char * const annotate_usage[] = { +static const char * const trace_usage[] = { "perf trace [<options>] <command>", NULL }; @@ -581,7 +581,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) setup_scripting(); - argc = parse_options(argc, argv, options, annotate_usage, + argc = parse_options(argc, argv, options, trace_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (symbol__init() < 0) -- cgit v1.2.3-70-g09d2