From 45de34bbe3e1b8f4c8bc8ecaf6c915b4b4c545f8 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Tue, 1 Jun 2010 21:25:01 +0200 Subject: perf buildid: add perfconfig option to specify buildid cache dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the ability to specify an alternate directory to store the buildid cache (buildids, copy of binaries). By default, it is hardcoded to $HOME/.debug. This directory contains immutable data. The layout of the directory is such that no conflicts in filenames are possible. A modification in a file, yields a different buildid and thus a different location in the subdir hierarchy. You may want to put the buildid cache elsewhere because of disk space limitation or simply to share the cache between users. It is also useful for remote collect vs. local analysis of profiles. This patch adds a new config option to the perfconfig file. Under the tag 'buildid', there is a dir option. For instance, if you have: $ cat /etc/perfconfig [buildid] dir = /var/cache/perf-buildid All buildids and binaries are be saved in the directory specified. The perf record, buildid-list, buildid-cache, report, annotate, and archive commands will it to pull information out. The option can be set in the system-wide perfconfig file or in the $HOME/.perfconfig file. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: <4c055fb7.df0ce30a.5f0d.ffffae52@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 5e02d2c17154..34760a2fc606 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -9,8 +9,6 @@ #include #include -#define DEBUG_CACHE_DIR ".debug" - #ifdef HAVE_CPLUS_DEMANGLE extern char *cplus_demangle(const char *, int); -- cgit v1.2.3 From f60f359383edf2a0ec3aa32cf8be98ad815bdf65 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Fri, 4 Jun 2010 11:27:10 -0300 Subject: perf report: Implement --sort cpu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a shared multi-core environment, users want to analyze why their program was slow. In particular, if the code ran slower only on certain CPUs due to interference from other programs or kernel threads, the user should be able to notice that. Sample usage: perf record -f -a -- sleep 3 perf report --sort cpu,comm Workload: program is running on 16 CPUs Experiencing interference from an antagonist only on 4 CPUs. Samples: 106218177676 cycles Overhead CPU Command ........ ... ............... 6.25% 2 program 6.24% 6 program 6.24% 11 program 6.24% 5 program 6.24% 9 program 6.24% 10 program 6.23% 15 program 6.23% 7 program 6.23% 3 program 6.23% 14 program 6.22% 1 program 6.20% 13 program 3.17% 12 program 3.15% 8 program 3.14% 0 program 3.13% 4 program 3.11% 4 antagonist 3.11% 0 antagonist 3.10% 8 antagonist 3.07% 12 antagonist Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: <20100505181612.GA5091@sharma-home.net> Signed-off-by: Arun Sharma Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 3 +++ tools/perf/util/event.c | 1 + tools/perf/util/hist.c | 1 + tools/perf/util/sort.c | 27 +++++++++++++++++++++++++++ tools/perf/util/sort.h | 6 +++++- tools/perf/util/symbol.h | 3 ++- 6 files changed, 39 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f28c4bbd801f..5e5c6403a315 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -274,6 +274,9 @@ static void create_counter(int counter, int cpu) if (call_graph) attr->sample_type |= PERF_SAMPLE_CALLCHAIN; + if (system_wide) + attr->sample_type |= PERF_SAMPLE_CPU; + if (raw_samples) { attr->sample_type |= PERF_SAMPLE_TIME; attr->sample_type |= PERF_SAMPLE_RAW; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index ed3e14ff6df0..a7460868124b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -710,6 +710,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : ""); al->sym = NULL; + al->cpu = data->cpu; if (al->map) { if (symbol_conf.dso_list && diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9e6baad92c4a..68d288c975de 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -70,6 +70,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, .map = al->map, .sym = al->sym, }, + .cpu = al->cpu, .ip = al->addr, .level = al->level, .period = period, diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2316cb5a4116..c27b4b03fbc1 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -13,6 +13,7 @@ enum sort_type sort__first_dimension; unsigned int dsos__col_width; unsigned int comms__col_width; unsigned int threads__col_width; +unsigned int cpus__col_width; static unsigned int parent_symbol__col_width; char * field_sep; @@ -28,6 +29,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width); static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width); +static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); struct sort_entry sort_thread = { .se_header = "Command: Pid", @@ -63,6 +66,13 @@ struct sort_entry sort_parent = { .se_snprintf = hist_entry__parent_snprintf, .se_width = &parent_symbol__col_width, }; + +struct sort_entry sort_cpu = { + .se_header = "CPU", + .se_cmp = sort__cpu_cmp, + .se_snprintf = hist_entry__cpu_snprintf, + .se_width = &cpus__col_width, +}; struct sort_dimension { const char *name; @@ -76,6 +86,7 @@ static struct sort_dimension sort_dimensions[] = { { .name = "dso", .entry = &sort_dso, }, { .name = "symbol", .entry = &sort_sym, }, { .name = "parent", .entry = &sort_parent, }, + { .name = "cpu", .entry = &sort_cpu, }, }; int64_t cmp_null(void *l, void *r) @@ -242,6 +253,20 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, self->parent ? self->parent->name : "[other]"); } +/* --sort cpu */ + +int64_t +sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return right->cpu - left->cpu; +} + +static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + return repsep_snprintf(bf, size, "%-*d", width, self->cpu); +} + int sort_dimension__add(const char *tok) { unsigned int i; @@ -281,6 +306,8 @@ int sort_dimension__add(const char *tok) sort__first_dimension = SORT_SYM; else if (!strcmp(sd->name, "parent")) sort__first_dimension = SORT_PARENT; + else if (!strcmp(sd->name, "cpu")) + sort__first_dimension = SORT_CPU; } list_add_tail(&sd->entry->list, &hist_entry__sort_list); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 0d61c4082f43..560c855417e4 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -39,6 +39,7 @@ extern struct sort_entry sort_parent; extern unsigned int dsos__col_width; extern unsigned int comms__col_width; extern unsigned int threads__col_width; +extern unsigned int cpus__col_width; extern enum sort_type sort__first_dimension; struct hist_entry { @@ -51,6 +52,7 @@ struct hist_entry { struct map_symbol ms; struct thread *thread; u64 ip; + s32 cpu; u32 nr_events; char level; u8 filtered; @@ -68,7 +70,8 @@ enum sort_type { SORT_COMM, SORT_DSO, SORT_SYM, - SORT_PARENT + SORT_PARENT, + SORT_CPU, }; /* @@ -104,6 +107,7 @@ extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); +int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); 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, diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 34760a2fc606..10b7ff859ce0 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -110,7 +110,8 @@ struct addr_location { u64 addr; char level; bool filtered; - unsigned int cpumode; + u8 cpumode; + s32 cpu; }; enum dso_kernel_type { -- cgit v1.2.3 From 9ed7e1b85cd55dc46cb9410a23086bdaa2ff3eb9 Mon Sep 17 00:00:00 2001 From: Chase Douglas Date: Mon, 14 Jun 2010 15:26:30 -0400 Subject: perf probe: Add kernel source path option The probe plugin requires access to the source code for some operations. The source code must be in the exact same location as specified by the DWARF tags, but sometimes the location is an absolute path that cannot be replicated by a normal user. This change adds the -s|--source option to allow the user to specify the root of the kernel source tree. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Masami Hiramatsu LKML-Reference: <1276543590-10486-1-git-send-email-chase.douglas@canonical.com> Signed-off-by: Chase Douglas Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-probe.txt | 4 +++ tools/perf/builtin-probe.c | 2 ++ tools/perf/util/probe-finder.c | 58 +++++++++++++++++++++++++++++++-- tools/perf/util/symbol.h | 1 + 4 files changed, 62 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 94a258c96a44..ea531d9d975c 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -31,6 +31,10 @@ OPTIONS --vmlinux=PATH:: Specify vmlinux path which has debuginfo (Dwarf binary). +-s:: +--source=PATH:: + Specify path to kernel source. + -v:: --verbose:: Be more verbose (show parsed arguments, etc). diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index e4a4da32a568..54551867e7e0 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -182,6 +182,8 @@ static const struct option options[] = { "Show source code lines.", opt_show_lines), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), + OPT_STRING('s', "source", &symbol_conf.source_prefix, + "directory", "path to kernel source"), #endif OPT__DRY_RUN(&probe_event_dry_run), OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index d964cb199c67..baf665383498 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -37,6 +37,7 @@ #include "event.h" #include "debug.h" #include "util.h" +#include "symbol.h" #include "probe-finder.h" /* Kprobe tracer basic type is up to u64 */ @@ -57,6 +58,55 @@ static int strtailcmp(const char *s1, const char *s2) return 0; } +/* + * Find a src file from a DWARF tag path. Prepend optional source path prefix + * and chop off leading directories that do not exist. Result is passed back as + * a newly allocated path on success. + * Return 0 if file was found and readable, -errno otherwise. + */ +static int get_real_path(const char *raw_path, char **new_path) +{ + if (!symbol_conf.source_prefix) { + if (access(raw_path, R_OK) == 0) { + *new_path = strdup(raw_path); + return 0; + } else + return -errno; + } + + *new_path = malloc((strlen(symbol_conf.source_prefix) + + strlen(raw_path) + 2)); + if (!*new_path) + return -ENOMEM; + + for (;;) { + sprintf(*new_path, "%s/%s", symbol_conf.source_prefix, + raw_path); + + if (access(*new_path, R_OK) == 0) + return 0; + + switch (errno) { + case ENAMETOOLONG: + case ENOENT: + case EROFS: + case EFAULT: + raw_path = strchr(++raw_path, '/'); + if (!raw_path) { + free(*new_path); + *new_path = NULL; + return -ENOENT; + } + continue; + + default: + free(*new_path); + *new_path = NULL; + return -errno; + } + } +} + /* Line number list operations */ /* Add a line to line number list */ @@ -1096,11 +1146,13 @@ end: static int line_range_add_line(const char *src, unsigned int lineno, struct line_range *lr) { + int ret; + /* Copy real path */ if (!lr->path) { - lr->path = strdup(src); - if (lr->path == NULL) - return -ENOMEM; + ret = get_real_path(src, &lr->path); + if (ret != 0) + return ret; } return line_list__add_line(&lr->line_list, lineno); } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 10b7ff859ce0..80e569bbdecc 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -71,6 +71,7 @@ struct symbol_conf { full_paths, show_cpu_utilization; const char *vmlinux_name, + *source_prefix, *field_sep; const char *default_guest_vmlinux_name, *default_guest_kallsyms, -- cgit v1.2.3 From 8a6c5b261c1188379469807d84bfb1365d0f6823 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 20 Jul 2010 14:42:52 -0300 Subject: perf sort: Make column width code per hists instance They were globals, and since we support multiple hists and sessions at the same time, it doesn't make sense to calculate those values considereing all symbols in all sessions. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 34 +++++++------- tools/perf/util/hist.c | 115 ++++++++++++++++++++++++++++++++--------------- tools/perf/util/hist.h | 27 ++++++++--- tools/perf/util/newt.c | 9 ++-- tools/perf/util/sort.c | 17 +++---- tools/perf/util/sort.h | 6 +-- tools/perf/util/symbol.c | 9 ++++ tools/perf/util/symbol.h | 2 + 8 files changed, 140 insertions(+), 79 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index d7f21d71eb69..121339f4360d 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -340,30 +340,29 @@ int event__synthesize_kernel_mmap(event__handler_t process, return process(&ev, session); } -static void thread__comm_adjust(struct thread *self) +static void thread__comm_adjust(struct thread *self, struct hists *hists) { char *comm = self->comm; 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); + u16 slen = strlen(comm); - if (slen > comms__col_width) { - comms__col_width = slen; - threads__col_width = slen + 6; - } + if (hists__new_col_len(hists, HISTC_COMM, slen)) + hists__set_col_len(hists, HISTC_THREAD, slen + 6); } } -static int thread__set_comm_adjust(struct thread *self, const char *comm) +static int thread__set_comm_adjust(struct thread *self, const char *comm, + struct hists *hists) { int ret = thread__set_comm(self, comm); if (ret) return ret; - thread__comm_adjust(self); + thread__comm_adjust(self, hists); return 0; } @@ -374,7 +373,8 @@ int event__process_comm(event_t *self, struct perf_session *session) dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); - if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { + if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, + &session->hists)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } @@ -641,16 +641,13 @@ void thread__find_addr_location(struct thread *self, al->sym = NULL; } -static void dso__calc_col_width(struct dso *self) +static void dso__calc_col_width(struct dso *self, struct hists *hists) { if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.dso_list || strlist__has_entry(symbol_conf.dso_list, self->name))) { - u16 slen = self->short_name_len; - if (verbose) - slen = self->long_name_len; - if (dsos__col_width < slen) - dsos__col_width = slen; + u16 slen = dso__name_len(self); + hists__new_col_len(hists, HISTC_DSO, slen); } self->slen_calculated = 1; @@ -729,16 +726,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, * sampled. */ if (!sort_dso.elide && !al->map->dso->slen_calculated) - dso__calc_col_width(al->map->dso); + dso__calc_col_width(al->map->dso, &session->hists); al->sym = map__find_symbol(al->map, al->addr, filter); } else { const unsigned int unresolved_col_width = BITS_PER_LONG / 4; - if (dsos__col_width < unresolved_col_width && + if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && !symbol_conf.col_width_list_str && !symbol_conf.field_sep && !symbol_conf.dso_list) - dsos__col_width = unresolved_col_width; + hists__set_col_len(&session->hists, HISTC_DSO, + unresolved_col_width); } if (symbol_conf.sym_list && al->sym && diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d998d1d706eb..0bc67900352c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -16,6 +16,50 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; +u16 hists__col_len(struct hists *self, enum hist_column col) +{ + return self->col_len[col]; +} + +void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) +{ + self->col_len[col] = len; +} + +bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) +{ + if (len > hists__col_len(self, col)) { + hists__set_col_len(self, col, len); + return true; + } + return false; +} + +static void hists__reset_col_len(struct hists *self) +{ + enum hist_column col; + + for (col = 0; col < HISTC_NR_COLS; ++col) + hists__set_col_len(self, col, 0); +} + +static void hists__calc_col_len(struct hists *self, struct hist_entry *h) +{ + u16 len; + + if (h->ms.sym) + hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); + + len = thread__comm_len(h->thread); + if (hists__new_col_len(self, HISTC_COMM, len)) + hists__set_col_len(self, HISTC_THREAD, len + 6); + + if (h->ms.map) { + len = dso__name_len(h->ms.map->dso); + hists__new_col_len(self, HISTC_DSO, len); + } +} + static void hist_entry__add_cpumode_period(struct hist_entry *self, unsigned int cpumode, u64 period) { @@ -56,13 +100,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return self; } -static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) +static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) { - if (entry->filtered) - return; - if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) - self->max_sym_namelen = entry->ms.sym->namelen; - ++self->nr_entries; + if (!h->filtered) { + hists__calc_col_len(self, h); + ++self->nr_entries; + } } static u8 symbol__parent_filter(const struct symbol *parent) @@ -208,7 +251,7 @@ void hists__collapse_resort(struct hists *self) tmp = RB_ROOT; next = rb_first(&self->entries); self->nr_entries = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); while (next) { n = rb_entry(next, struct hist_entry, rb_node); @@ -265,7 +308,7 @@ void hists__output_resort(struct hists *self) next = rb_first(&self->entries); self->nr_entries = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); while (next) { n = rb_entry(next, struct hist_entry, rb_node); @@ -532,8 +575,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, } int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, - struct hists *pair_hists, bool show_displacement, - long displacement, bool color, u64 session_total) + struct hists *hists, struct hists *pair_hists, + bool show_displacement, long displacement, + bool color, u64 session_total) { struct sort_entry *se; u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; @@ -637,24 +681,25 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); ret += se->se_snprintf(self, s + ret, size - ret, - se->se_width ? *se->se_width : 0); + hists__col_len(hists, se->se_width_idx)); } return ret; } -int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, - bool show_displacement, long displacement, FILE *fp, - u64 session_total) +int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, + struct hists *pair_hists, bool show_displacement, + long displacement, FILE *fp, u64 session_total) { char bf[512]; - hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, + hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, show_displacement, displacement, true, session_total); return fprintf(fp, "%s\n", bf); } -static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, +static size_t hist_entry__fprintf_callchain(struct hist_entry *self, + struct hists *hists, FILE *fp, u64 session_total) { int left_margin = 0; @@ -662,7 +707,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, if (sort__first_dimension == SORT_COMM) { struct sort_entry *se = list_first_entry(&hist_entry__sort_list, typeof(*se), list); - left_margin = se->se_width ? *se->se_width : 0; + left_margin = hists__col_len(hists, se->se_width_idx); left_margin -= thread__comm_len(self->thread); } @@ -733,17 +778,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, continue; } width = strlen(se->se_header); - if (se->se_width) { - if (symbol_conf.col_width_list_str) { - if (col_width) { - *se->se_width = atoi(col_width); - col_width = strchr(col_width, ','); - if (col_width) - ++col_width; - } + if (symbol_conf.col_width_list_str) { + if (col_width) { + hists__set_col_len(self, se->se_width_idx, + atoi(col_width)); + col_width = strchr(col_width, ','); + if (col_width) + ++col_width; } - width = *se->se_width = max(*se->se_width, width); } + if (!hists__new_col_len(self, se->se_width_idx, width)) + width = hists__col_len(self, se->se_width_idx); fprintf(fp, " %*s", width, se->se_header); } fprintf(fp, "\n"); @@ -766,9 +811,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, continue; fprintf(fp, " "); - if (se->se_width) - width = *se->se_width; - else + width = hists__col_len(self, se->se_width_idx); + if (width == 0) width = strlen(se->se_header); for (i = 0; i < width; i++) fprintf(fp, "."); @@ -788,12 +832,12 @@ print_entries: displacement = 0; ++position; } - ret += hist_entry__fprintf(h, pair, show_displacement, + ret += hist_entry__fprintf(h, self, pair, show_displacement, displacement, fp, self->stats.total_period); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); - + ret += hist_entry__fprintf_callchain(h, self, fp, + self->stats.total_period); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, verbose, fp); @@ -817,8 +861,7 @@ static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, self->stats.total_period += h->period; self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; - if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) - self->max_sym_namelen = h->ms.sym->namelen; + hists__calc_col_len(self, h); } void hists__filter_by_dso(struct hists *self, const struct dso *dso) @@ -827,7 +870,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) self->nr_entries = self->stats.total_period = 0; self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); @@ -850,7 +893,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) self->nr_entries = self->stats.total_period = 0; self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 83fa33a7b38b..92962b2f579b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -56,6 +56,16 @@ struct events_stats { u32 nr_unknown_events; }; +enum hist_column { + HISTC_SYMBOL, + HISTC_DSO, + HISTC_THREAD, + HISTC_COMM, + HISTC_PARENT, + HISTC_CPU, + HISTC_NR_COLS, /* Last entry */ +}; + struct hists { struct rb_node rb_node; struct rb_root entries; @@ -64,7 +74,7 @@ struct hists { u64 config; u64 event_stream; u32 type; - u32 max_sym_namelen; + u16 col_len[HISTC_NR_COLS]; }; struct hist_entry *__hists__add_entry(struct hists *self, @@ -72,12 +82,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, struct symbol *parent, u64 period); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, - bool show_displacement, long displacement, FILE *fp, - u64 total); +int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, + struct hists *pair_hists, bool show_displacement, + long displacement, FILE *fp, u64 total); int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, - struct hists *pair_hists, bool show_displacement, - long displacement, bool color, u64 total); + struct hists *hists, struct hists *pair_hists, + bool show_displacement, long displacement, + bool color, u64 total); void hist_entry__free(struct hist_entry *); void hists__output_resort(struct hists *self); @@ -95,6 +106,10 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head); void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); +u16 hists__col_len(struct hists *self, enum hist_column col); +void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); +bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); + #ifdef NO_NEWT_SUPPORT static inline int hists__browse(struct hists *self __used, const char *helpline __used, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 7979003adeaf..ab6eb368cbf5 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -692,7 +692,8 @@ static void hist_entry__append_callchain_browser(struct hist_entry *self, } static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent tree, u64 total) + newtComponent tree, + struct hists *hists) { char s[256]; size_t ret; @@ -700,8 +701,8 @@ static size_t hist_entry__append_browser(struct hist_entry *self, if (symbol_conf.exclude_other && !self->parent) return 0; - ret = hist_entry__snprintf(self, s, sizeof(s), NULL, - false, 0, false, total); + ret = hist_entry__snprintf(self, s, sizeof(s), hists, NULL, + false, 0, false, hists->stats.total_period); if (symbol_conf.use_callchain) { int indexes[2]; @@ -842,7 +843,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); + len = hist_entry__append_browser(h, self->tree, hists); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index c27b4b03fbc1..1c61a4f4aa8a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,4 +1,5 @@ #include "sort.h" +#include "hist.h" regex_t parent_regex; const char default_parent_pattern[] = "^sys_|^do_page_fault"; @@ -10,11 +11,6 @@ int sort__has_parent = 0; enum sort_type sort__first_dimension; -unsigned int dsos__col_width; -unsigned int comms__col_width; -unsigned int threads__col_width; -unsigned int cpus__col_width; -static unsigned int parent_symbol__col_width; char * field_sep; LIST_HEAD(hist_entry__sort_list); @@ -36,7 +32,7 @@ struct sort_entry sort_thread = { .se_header = "Command: Pid", .se_cmp = sort__thread_cmp, .se_snprintf = hist_entry__thread_snprintf, - .se_width = &threads__col_width, + .se_width_idx = HISTC_THREAD, }; struct sort_entry sort_comm = { @@ -44,34 +40,35 @@ struct sort_entry sort_comm = { .se_cmp = sort__comm_cmp, .se_collapse = sort__comm_collapse, .se_snprintf = hist_entry__comm_snprintf, - .se_width = &comms__col_width, + .se_width_idx = HISTC_COMM, }; struct sort_entry sort_dso = { .se_header = "Shared Object", .se_cmp = sort__dso_cmp, .se_snprintf = hist_entry__dso_snprintf, - .se_width = &dsos__col_width, + .se_width_idx = HISTC_DSO, }; struct sort_entry sort_sym = { .se_header = "Symbol", .se_cmp = sort__sym_cmp, .se_snprintf = hist_entry__sym_snprintf, + .se_width_idx = HISTC_SYMBOL, }; struct sort_entry sort_parent = { .se_header = "Parent symbol", .se_cmp = sort__parent_cmp, .se_snprintf = hist_entry__parent_snprintf, - .se_width = &parent_symbol__col_width, + .se_width_idx = HISTC_PARENT, }; struct sort_entry sort_cpu = { .se_header = "CPU", .se_cmp = sort__cpu_cmp, .se_snprintf = hist_entry__cpu_snprintf, - .se_width = &cpus__col_width, + .se_width_idx = HISTC_CPU, }; struct sort_dimension { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 560c855417e4..03a1722e6d02 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -36,10 +36,6 @@ extern struct sort_entry sort_comm; extern struct sort_entry sort_dso; extern struct sort_entry sort_sym; extern struct sort_entry sort_parent; -extern unsigned int dsos__col_width; -extern unsigned int comms__col_width; -extern unsigned int threads__col_width; -extern unsigned int cpus__col_width; extern enum sort_type sort__first_dimension; struct hist_entry { @@ -87,7 +83,7 @@ struct sort_entry { int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, unsigned int width); - unsigned int *se_width; + u8 se_width_idx; bool elide; }; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 971d0a05d6b4..bc6e7e8c480d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -12,6 +12,7 @@ #include #include #include "build-id.h" +#include "debug.h" #include "symbol.h" #include "strlist.h" @@ -40,6 +41,14 @@ struct symbol_conf symbol_conf = { .try_vmlinux_path = true, }; +int dso__name_len(const struct dso *self) +{ + if (verbose) + return self->long_name_len; + + return self->short_name_len; +} + bool dso__loaded(const struct dso *self, enum map_type type) { return self->loaded & (1 << type); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 80e569bbdecc..d436ee3d3a73 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -146,6 +146,8 @@ struct dso *dso__new(const char *name); struct dso *dso__new_kernel(const char *name); void dso__delete(struct dso *self); +int dso__name_len(const struct dso *self); + bool dso__loaded(const struct dso *self, enum map_type type); bool dso__sorted_by_name(const struct dso *self, enum map_type type); -- cgit v1.2.3 From 0f0cbf7aa3d3460a3eb201a772326739a0c0210a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 26 Jul 2010 17:13:40 -0300 Subject: perf ui: New hists tree widget The stock newt checkbox tree widget we were using was not really suitable for hist entry + callchain browsing. The problems with it were manifold: - We needed to traverse the whole hist_entry rb_tree to add each entry + callchains beforehand. - No control over the colors used for each row So a new tree widget, based mostly on slang, was written. It extends the ui_browser class already used for annotate to allow the user to fold/unfold branches in the callchains tree, using extra fields in the symbol_map class that is embedded in hist_entry and callchain_node instances to store the folding state and when changing this state calculates the number of rows that are produced when showing a particular hist_entry instance. This greatly speeds up browsing as we don't have to upfront touch all the entries and only calculate callchain related operations when some callchain branch is actually unfolded. The memory footprint is also reduced as the data structure is not duplicated, just some extra fields for controling callchain state and to simplify the process of seeking thru entries (nr_rows, row_offset) were added. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 3 + tools/perf/util/newt.c | 972 ++++++++++++++++++++++++++++++++--------------- tools/perf/util/sort.h | 12 + tools/perf/util/symbol.h | 2 + 4 files changed, 678 insertions(+), 311 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f93095ffaab0..d0f07f7bdf16 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -885,6 +885,9 @@ static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, return; ++self->nr_entries; + if (h->ms.unfolded) + self->nr_entries += h->nr_rows; + h->row_offset = 0; self->stats.total_period += h->period; self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index cdd958eb68cf..28f74eb8fe7c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -509,38 +509,6 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) return 0; } -/* - * When debugging newt problems it was useful to be able to "unroll" - * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate - * a source file with the sequence of calls to these methods, to then - * tweak the arrays to get the intended results, so I'm keeping this code - * here, may be useful again in the future. - */ -#undef NEWT_DEBUG - -static void newt_checkbox_tree__add(newtComponent tree, const char *str, - void *priv, int *indexes) -{ -#ifdef NEWT_DEBUG - /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ - int i = 0, len = 40 - strlen(str); - - fprintf(stderr, - "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", - len, len, " ", str, priv); - while (indexes[i] != NEWT_ARG_LAST) { - if (indexes[i] != NEWT_ARG_APPEND) - fprintf(stderr, " %d,", indexes[i]); - else - fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); - ++i; - } - fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); - fflush(stderr); -#endif - newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); -} - static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { @@ -576,147 +544,6 @@ static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self return row; } -static void __callchain__append_graph_browser(struct callchain_node *self, - newtComponent tree, u64 total, - int *indexes, int depth) -{ - struct rb_node *node; - u64 new_total, remaining; - int idx = 0; - - if (callchain_param.mode == CHAIN_GRAPH_REL) - new_total = self->children_hit; - else - new_total = total; - - remaining = new_total; - node = rb_first(&self->rb_root); - while (node) { - struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); - struct rb_node *next = rb_next(node); - u64 cumul = cumul_hits(child); - struct callchain_list *chain; - int first = true, printed = 0; - int chain_idx = -1; - remaining -= cumul; - - indexes[depth] = NEWT_ARG_APPEND; - indexes[depth + 1] = NEWT_ARG_LAST; - - list_for_each_entry(chain, &child->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], - *alloc_str = NULL; - const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - - if (first) { - double percent = cumul * 100.0 / new_total; - - first = false; - if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) - str = "Not enough memory!"; - else - str = alloc_str; - } else { - indexes[depth] = idx; - indexes[depth + 1] = NEWT_ARG_APPEND; - indexes[depth + 2] = NEWT_ARG_LAST; - ++chain_idx; - } - newt_checkbox_tree__add(tree, str, &chain->ms, indexes); - free(alloc_str); - ++printed; - } - - indexes[depth] = idx; - if (chain_idx != -1) - indexes[depth + 1] = chain_idx; - if (printed != 0) - ++idx; - __callchain__append_graph_browser(child, tree, new_total, indexes, - depth + (chain_idx != -1 ? 2 : 1)); - node = next; - } -} - -static void callchain__append_graph_browser(struct callchain_node *self, - newtComponent tree, u64 total, - int *indexes, int parent_idx) -{ - struct callchain_list *chain; - int i = 0; - - indexes[1] = NEWT_ARG_APPEND; - indexes[2] = NEWT_ARG_LAST; - - list_for_each_entry(chain, &self->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], *str; - - if (chain->ip >= PERF_CONTEXT_MAX) - continue; - - if (!i++ && sort__first_dimension == SORT_SYM) - continue; - - str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, &chain->ms, indexes); - } - - indexes[1] = parent_idx; - indexes[2] = NEWT_ARG_APPEND; - indexes[3] = NEWT_ARG_LAST; - __callchain__append_graph_browser(self, tree, total, indexes, 2); -} - -static void hist_entry__append_callchain_browser(struct hist_entry *self, - newtComponent tree, u64 total, int parent_idx) -{ - struct rb_node *rb_node; - int indexes[1024] = { [0] = parent_idx, }; - int idx = 0; - struct callchain_node *chain; - - rb_node = rb_first(&self->sorted_chain); - while (rb_node) { - chain = rb_entry(rb_node, struct callchain_node, rb_node); - switch (callchain_param.mode) { - case CHAIN_FLAT: - break; - case CHAIN_GRAPH_ABS: /* falldown */ - case CHAIN_GRAPH_REL: - callchain__append_graph_browser(chain, tree, total, indexes, idx++); - break; - case CHAIN_NONE: - default: - break; - } - rb_node = rb_next(rb_node); - } -} - -static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent tree, - struct hists *hists) -{ - char s[256]; - size_t ret; - - if (symbol_conf.exclude_other && !self->parent) - return 0; - - ret = hist_entry__snprintf(self, s, sizeof(s), hists, NULL, - false, 0, false, hists->stats.total_period); - if (symbol_conf.use_callchain) { - int indexes[2]; - - indexes[0] = NEWT_ARG_APPEND; - indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, &self->ms, indexes); - } else - newtListboxAppendEntry(tree, s, &self->ms); - - return ret; -} - int hist_entry__tui_annotate(struct hist_entry *self) { struct ui_browser browser; @@ -764,157 +591,48 @@ int hist_entry__tui_annotate(struct hist_entry *self) return ret; } -static const void *newt__symbol_tree_get_current(newtComponent self) -{ - if (symbol_conf.use_callchain) - return newtCheckboxTreeGetCurrent(self); - return newtListboxGetCurrent(self); -} - -static void hist_browser__selection(newtComponent self, void *data) -{ - const struct map_symbol **symbol_ptr = data; - *symbol_ptr = newt__symbol_tree_get_current(self); -} - struct hist_browser { - newtComponent form, tree; - const struct map_symbol *selection; + struct ui_browser b; + struct hists *hists; + struct hist_entry *he_selection; + struct map_symbol *selection; }; -static struct hist_browser *hist_browser__new(void) +static void hist_browser__reset(struct hist_browser *self); +static int hist_browser__run(struct hist_browser *self, const char *title, + struct newtExitStruct *es); +static unsigned int hist_browser__refresh_entries(struct ui_browser *self); +static void ui_browser__hists_seek(struct ui_browser *self, + off_t offset, int whence); + +static struct hist_browser *hist_browser__new(struct hists *hists) { - struct hist_browser *self = malloc(sizeof(*self)); + struct hist_browser *self = zalloc(sizeof(*self)); - if (self != NULL) - self->form = NULL; + if (self) { + self->hists = hists; + self->b.refresh_entries = hist_browser__refresh_entries; + self->b.seek = ui_browser__hists_seek; + } return self; } static void hist_browser__delete(struct hist_browser *self) { - newtFormDestroy(self->form); + newtFormDestroy(self->b.form); newtPopWindow(); free(self); } -static int hist_browser__populate(struct hist_browser *self, struct hists *hists, - const char *title) -{ - int max_len = 0, idx, cols, rows; - struct ui_progress *progress; - struct rb_node *nd; - u64 curr_hist = 0; - char seq[] = ".", unit; - char str[256]; - unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; - - if (self->form) { - newtFormDestroy(self->form); - newtPopWindow(); - } - - nr_events = convert_unit(nr_events, &unit); - snprintf(str, sizeof(str), "Events: %lu%c ", - nr_events, unit); - newtDrawRootText(0, 0, str); - - newtGetScreenSize(NULL, &rows); - - if (symbol_conf.use_callchain) - self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, - NEWT_FLAG_SCROLL); - else - self->tree = newtListbox(0, 0, rows - 5, - (NEWT_FLAG_SCROLL | - NEWT_FLAG_RETURNEXIT)); - - newtComponentAddCallback(self->tree, hist_browser__selection, - &self->selection); - - progress = ui_progress__new("Adding entries to the browser...", - hists->nr_entries); - if (progress == NULL) - return -1; - - idx = 0; - for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - int len; - - if (h->filtered) - continue; - - len = hist_entry__append_browser(h, self->tree, hists); - if (len > max_len) - max_len = len; - if (symbol_conf.use_callchain) - hist_entry__append_callchain_browser(h, self->tree, - hists->stats.total_period, idx++); - ++curr_hist; - if (curr_hist % 5) - ui_progress__update(progress, curr_hist); - } - - ui_progress__delete(progress); - - newtGetScreenSize(&cols, &rows); - - if (max_len > cols) - max_len = cols - 3; - - if (!symbol_conf.use_callchain) - newtListboxSetWidth(self->tree, max_len); - - newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), - rows - 5, title); - self->form = newt_form__new(); - if (self->form == NULL) - return -1; - - newtFormAddHotKey(self->form, 'A'); - newtFormAddHotKey(self->form, 'a'); - newtFormAddHotKey(self->form, 'D'); - newtFormAddHotKey(self->form, 'd'); - newtFormAddHotKey(self->form, 'T'); - newtFormAddHotKey(self->form, 't'); - newtFormAddHotKey(self->form, '?'); - newtFormAddHotKey(self->form, 'H'); - newtFormAddHotKey(self->form, 'h'); - newtFormAddHotKey(self->form, NEWT_KEY_F1); - newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); - newtFormAddHotKey(self->form, NEWT_KEY_TAB); - newtFormAddHotKey(self->form, NEWT_KEY_UNTAB); - newtFormAddComponents(self->form, self->tree, NULL); - self->selection = newt__symbol_tree_get_current(self->tree); - - return 0; -} - static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) { - int *indexes; - - if (!symbol_conf.use_callchain) - goto out; - - indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); - if (indexes) { - bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; - free(indexes); - if (is_hist_entry) - goto out; - } - return NULL; -out: - return container_of(self->selection, struct hist_entry, ms); + return self->he_selection; } static struct thread *hist_browser__selected_thread(struct hist_browser *self) { - struct hist_entry *he = hist_browser__selected_entry(self); - return he ? he->thread : NULL; + return self->he_selection->thread; } static int hist_browser__title(char *bf, size_t size, const char *ev_name, @@ -936,7 +654,7 @@ static int hist_browser__title(char *bf, size_t size, const char *ev_name, int hists__browse(struct hists *self, const char *helpline, const char *ev_name) { - struct hist_browser *browser = hist_browser__new(); + struct hist_browser *browser = hist_browser__new(self); struct pstack *fstack; const struct thread *thread_filter = NULL; const struct dso *dso_filter = NULL; @@ -955,8 +673,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out_free_stack; while (1) { const struct thread *thread; @@ -965,7 +681,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) int nr_options = 0, choice = 0, i, annotate = -2, zoom_dso = -2, zoom_thread = -2; - newtFormRun(browser->form, &es); + if (hist_browser__run(browser, msg, &es)) + break; thread = hist_browser__selected_thread(browser); dso = browser->selection->map ? browser->selection->map->dso : NULL; @@ -1100,8 +817,7 @@ zoom_out_dso: hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out; + hist_browser__reset(browser); } else if (choice == zoom_thread) { zoom_thread: if (thread_filter) { @@ -1119,8 +835,7 @@ zoom_out_thread: hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out; + hist_browser__reset(browser); } } out_free_stack: @@ -1207,3 +922,638 @@ void exit_browser(bool wait_for_ok) newtFinished(); } } + +static void hist_browser__refresh_dimensions(struct hist_browser *self) +{ + /* 3 == +/- toggle symbol before actual hist_entry rendering */ + self->b.width = 3 + (hists__sort_list_width(self->hists) + + sizeof("[k]")); +} + +static void hist_browser__reset(struct hist_browser *self) +{ + self->b.nr_entries = self->hists->nr_entries; + hist_browser__refresh_dimensions(self); + ui_browser__reset_index(&self->b); +} + +static char tree__folded_sign(bool unfolded) +{ + return unfolded ? '-' : '+'; +} + +static char map_symbol__folded(const struct map_symbol *self) +{ + return self->has_children ? tree__folded_sign(self->unfolded) : ' '; +} + +static char hist_entry__folded(const struct hist_entry *self) +{ + return map_symbol__folded(&self->ms); +} + +static char callchain_list__folded(const struct callchain_list *self) +{ + return map_symbol__folded(&self->ms); +} + +static bool map_symbol__toggle_fold(struct map_symbol *self) +{ + if (!self->has_children) + return false; + + self->unfolded = !self->unfolded; + return true; +} + +#define LEVEL_OFFSET_STEP 3 + +static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, + struct callchain_node *chain_node, + u64 total, int level, + unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *node; + int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; + u64 new_total, remaining; + + if (callchain_param.mode == CHAIN_GRAPH_REL) + new_total = chain_node->children_hit; + else + new_total = total; + + remaining = new_total; + node = rb_first(&chain_node->rb_root); + while (node) { + struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); + struct rb_node *next = rb_next(node); + u64 cumul = cumul_hits(child); + struct callchain_list *chain; + char folded_sign = ' '; + int first = true; + int extra_offset = 0; + + remaining -= cumul; + + list_for_each_entry(chain, &child->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; + const char *str; + int color; + bool was_first = first; + + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } else { + extra_offset = LEVEL_OFFSET_STEP; + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + folded_sign = callchain_list__folded(chain); + if (*row_offset != 0) { + --*row_offset; + goto do_next; + } + + alloc_str = NULL; + str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + if (was_first) { + double percent = cumul * 100.0 / new_total; + + if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) + str = "Not enough memory!"; + else + str = alloc_str; + } + + color = HE_COLORSET_NORMAL; + width = self->b.width - (offset + extra_offset + 2); + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.top + row, self->b.left); + slsmg_write_nstring(" ", offset + extra_offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(str, width); + free(alloc_str); + + if (++row == self->b.height) + goto out; +do_next: + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') { + const int new_level = level + (extra_offset ? 2 : 1); + row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, + new_level, row, row_offset, + is_current_entry); + } + if (row == self->b.height) + goto out; + node = next; + } +out: + return row - first_row; +} + +static int hist_browser__show_callchain_node(struct hist_browser *self, + struct callchain_node *node, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct callchain_list *chain; + int first_row = row, + offset = level * LEVEL_OFFSET_STEP, + width = self->b.width - offset; + char folded_sign = ' '; + + list_for_each_entry(chain, &node->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *s; + int color; + /* + * FIXME: This should be moved to somewhere else, + * probably when the callchain is created, so as not to + * traverse it all over again + */ + chain->ms.has_children = rb_first(&node->rb_root) != NULL; + folded_sign = callchain_list__folded(chain); + + if (*row_offset != 0) { + --*row_offset; + continue; + } + + color = HE_COLORSET_NORMAL; + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_set_color(color); + slsmg_write_nstring(" ", offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(s, width - 2); + + if (++row == self->b.height) + goto out; + } + + if (folded_sign == '-') + row += hist_browser__show_callchain_node_rb_tree(self, node, + self->hists->stats.total_period, + level + 1, row, + row_offset, + is_current_entry); +out: + return row - first_row; +} + +static int hist_browser__show_callchain(struct hist_browser *self, + struct rb_root *chain, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *nd; + int first_row = row; + + for (nd = rb_first(chain); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + + row += hist_browser__show_callchain_node(self, node, level, + row, row_offset, + is_current_entry); + if (row == self->b.height) + break; + } + + return row - first_row; +} + +static int hist_browser__show_entry(struct hist_browser *self, + struct hist_entry *entry, + unsigned short row) +{ + char s[256]; + double percent; + int printed = 0; + int color, width = self->b.width; + char folded_sign = ' '; + bool current_entry = ui_browser__is_current_entry(&self->b, row); + off_t row_offset = entry->row_offset; + + if (current_entry) { + self->he_selection = entry; + self->selection = &entry->ms; + } + + if (symbol_conf.use_callchain) { + entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); + folded_sign = hist_entry__folded(entry); + } + + if (row_offset == 0) { + hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, + 0, false, self->hists->stats.total_period); + percent = (entry->period * 100.0) / self->hists->stats.total_period; + + color = HE_COLORSET_SELECTED; + if (!current_entry) { + if (percent >= MIN_RED) + color = HE_COLORSET_TOP; + else if (percent >= MIN_GREEN) + color = HE_COLORSET_MEDIUM; + else + color = HE_COLORSET_NORMAL; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.top + row, self->b.left); + if (symbol_conf.use_callchain) { + slsmg_printf("%c ", folded_sign); + width -= 2; + } + slsmg_write_nstring(s, width); + ++row; + ++printed; + } else + --row_offset; + + if (folded_sign == '-' && row != self->b.height) { + printed += hist_browser__show_callchain(self, &entry->sorted_chain, + 1, row, &row_offset, + ¤t_entry); + if (current_entry) + self->he_selection = entry; + } + + return printed; +} + +static unsigned int hist_browser__refresh_entries(struct ui_browser *self) +{ + unsigned row = 0; + struct rb_node *nd; + struct hist_browser *hb = container_of(self, struct hist_browser, b); + + if (self->first_visible_entry == NULL) + self->first_visible_entry = rb_first(&hb->hists->entries); + + for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (h->filtered) + continue; + + row += hist_browser__show_entry(hb, h, row); + if (row == self->height) + break; + } + + return row; +} + +static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) +{ + struct rb_node *nd = rb_first(&self->rb_root); + + for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { + struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); + struct callchain_list *chain; + int first = true; + + list_for_each_entry(chain, &child->val, list) { + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } else + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + callchain_node__init_have_children_rb_tree(child); + } +} + +static void callchain_node__init_have_children(struct callchain_node *self) +{ + struct callchain_list *chain; + + list_for_each_entry(chain, &self->val, list) + chain->ms.has_children = rb_first(&self->rb_root) != NULL; + + callchain_node__init_have_children_rb_tree(self); +} + +static void callchain__init_have_children(struct rb_root *self) +{ + struct rb_node *nd; + + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + callchain_node__init_have_children(node); + } +} + +static void hist_entry__init_have_children(struct hist_entry *self) +{ + if (!self->init_have_children) { + callchain__init_have_children(&self->sorted_chain); + self->init_have_children = true; + } +} + +static struct rb_node *hists__filter_entries(struct rb_node *nd) +{ + while (nd != NULL) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + if (!h->filtered) + return nd; + + nd = rb_next(nd); + } + + return NULL; +} + +static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) +{ + while (nd != NULL) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + if (!h->filtered) + return nd; + + nd = rb_prev(nd); + } + + return NULL; +} + +static void ui_browser__hists_seek(struct ui_browser *self, + off_t offset, int whence) +{ + struct hist_entry *h; + struct rb_node *nd; + bool first = true; + + switch (whence) { + case SEEK_SET: + nd = hists__filter_entries(rb_first(self->entries)); + break; + case SEEK_CUR: + nd = self->first_visible_entry; + goto do_offset; + case SEEK_END: + nd = hists__filter_prev_entries(rb_last(self->entries)); + first = false; + break; + default: + return; + } + + /* + * Moves not relative to the first visible entry invalidates its + * row_offset: + */ + h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); + h->row_offset = 0; + + /* + * Here we have to check if nd is expanded (+), if it is we can't go + * the next top level hist_entry, instead we must compute an offset of + * what _not_ to show and not change the first visible entry. + * + * This offset increments when we are going from top to bottom and + * decreases when we're going from bottom to top. + * + * As we don't have backpointers to the top level in the callchains + * structure, we need to always print the whole hist_entry callchain, + * skipping the first ones that are before the first visible entry + * and stop when we printed enough lines to fill the screen. + */ +do_offset: + if (offset > 0) { + do { + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) { + u16 remaining = h->nr_rows - h->row_offset; + if (offset > remaining) { + offset -= remaining; + h->row_offset = 0; + } else { + h->row_offset += offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } + nd = hists__filter_entries(rb_next(nd)); + if (nd == NULL) + break; + --offset; + self->first_visible_entry = nd; + } while (offset != 0); + } else if (offset < 0) { + while (1) { + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) { + if (first) { + if (-offset > h->row_offset) { + offset += h->row_offset; + h->row_offset = 0; + } else { + h->row_offset += offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } else { + if (-offset > h->nr_rows) { + offset += h->nr_rows; + h->row_offset = 0; + } else { + h->row_offset = h->nr_rows + offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } + } + + nd = hists__filter_prev_entries(rb_prev(nd)); + if (nd == NULL) + break; + ++offset; + self->first_visible_entry = nd; + if (offset == 0) { + /* + * Last unfiltered hist_entry, check if it is + * unfolded, if it is then we should have + * row_offset at its last entry. + */ + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) + h->row_offset = h->nr_rows; + break; + } + first = false; + } + } else { + self->first_visible_entry = nd; + h = rb_entry(nd, struct hist_entry, rb_node); + h->row_offset = 0; + } +} + +static int callchain_node__count_rows_rb_tree(struct callchain_node *self) +{ + int n = 0; + struct rb_node *nd; + + for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { + struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); + struct callchain_list *chain; + char folded_sign = ' '; /* No children */ + + list_for_each_entry(chain, &child->val, list) { + ++n; + /* We need this because we may not have children */ + folded_sign = callchain_list__folded(chain); + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') /* Have children and they're unfolded */ + n += callchain_node__count_rows_rb_tree(child); + } + + return n; +} + +static int callchain_node__count_rows(struct callchain_node *node) +{ + struct callchain_list *chain; + bool unfolded = false; + int n = 0; + + list_for_each_entry(chain, &node->val, list) { + ++n; + unfolded = chain->ms.unfolded; + } + + if (unfolded) + n += callchain_node__count_rows_rb_tree(node); + + return n; +} + +static int callchain__count_rows(struct rb_root *chain) +{ + struct rb_node *nd; + int n = 0; + + for (nd = rb_first(chain); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + n += callchain_node__count_rows(node); + } + + return n; +} + +static bool hist_browser__toggle_fold(struct hist_browser *self) +{ + if (map_symbol__toggle_fold(self->selection)) { + struct hist_entry *he = self->he_selection; + + hist_entry__init_have_children(he); + self->hists->nr_entries -= he->nr_rows; + + if (he->ms.unfolded) + he->nr_rows = callchain__count_rows(&he->sorted_chain); + else + he->nr_rows = 0; + self->hists->nr_entries += he->nr_rows; + self->b.nr_entries = self->hists->nr_entries; + + return true; + } + + /* If it doesn't have children, no toggling performed */ + return false; +} + +static int hist_browser__run(struct hist_browser *self, const char *title, + struct newtExitStruct *es) +{ + char str[256], unit; + unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; + + self->b.entries = &self->hists->entries; + self->b.nr_entries = self->hists->nr_entries; + + hist_browser__refresh_dimensions(self); + + nr_events = convert_unit(nr_events, &unit); + snprintf(str, sizeof(str), "Events: %lu%c ", + nr_events, unit); + newtDrawRootText(0, 0, str); + + if (ui_browser__show(&self->b, title) < 0) + return -1; + + newtFormAddHotKey(self->b.form, 'A'); + newtFormAddHotKey(self->b.form, 'a'); + newtFormAddHotKey(self->b.form, '?'); + newtFormAddHotKey(self->b.form, 'h'); + newtFormAddHotKey(self->b.form, 'H'); + newtFormAddHotKey(self->b.form, 'd'); + + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case 'd': { /* Debug */ + static int seq; + struct hist_entry *h = rb_entry(self->b.first_visible_entry, + struct hist_entry, rb_node); + ui_helpline__pop(); + ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", + seq++, self->b.nr_entries, + self->hists->nr_entries, + self->b.height, + self->b.index, + self->b.first_visible_entry_idx, + h->row_offset, h->nr_rows); + } + continue; + case NEWT_KEY_ENTER: + if (hist_browser__toggle_fold(self)) + break; + /* fall thru */ + default: + return 0; + } + } + return 0; +} diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 03a1722e6d02..46e531d09e8b 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -38,6 +38,12 @@ extern struct sort_entry sort_sym; extern struct sort_entry sort_parent; extern enum sort_type sort__first_dimension; +/** + * struct hist_entry - histogram entry + * + * @row_offset - offset from the first callchain expanded to appear on screen + * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding + */ struct hist_entry { struct rb_node rb_node; u64 period; @@ -50,6 +56,12 @@ struct hist_entry { u64 ip; s32 cpu; u32 nr_events; + + /* XXX These two should move to some tree widget lib */ + u16 row_offset; + u16 nr_rows; + + bool init_have_children; char level; u8 filtered; struct symbol *parent; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d436ee3d3a73..bb1aab9fa34f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -102,6 +102,8 @@ struct ref_reloc_sym { struct map_symbol { struct map *map; struct symbol *sym; + bool unfolded; + bool has_children; }; struct addr_location { -- cgit v1.2.3 From 88ca895dd4e0e64ebd942adb7925fa60ca5b2a98 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 27 Jul 2010 11:46:12 -0300 Subject: perf tools: Remove unneeded code for tracking the cwd in perf sessions Tidy-up patch to remove some code and struct perf_session data members which are no longer needed due to the previous patch: "perf tools: Don't abbreviate file paths relative to the cwd". LKML-Reference: Signed-off-by: Dave Martin Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-buildid-list.c | 4 +--- tools/perf/builtin-diff.c | 2 -- tools/perf/builtin-report.c | 2 -- tools/perf/util/session.c | 22 +--------------------- tools/perf/util/symbol.h | 1 - 5 files changed, 2 insertions(+), 29 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 99890728409e..44a47e13bd67 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -43,10 +43,8 @@ static int __cmd_buildid_list(void) if (session == NULL) return -1; - if (with_hits) { - symbol_conf.full_paths = true; + if (with_hits) perf_session__process_events(session, &build_id__mark_dso_hit_ops); - } perf_session__fprintf_dsos_buildid(session, stdout, with_hits); diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 39e6627ebb96..fca1d4402910 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -180,8 +180,6 @@ static const struct option options[] = { 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', "full-paths", &symbol_conf.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...]", diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index ce42bbaa252d..2f4b92925b26 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -441,8 +441,6 @@ static const struct option options[] = { "pretty printing style key: normal raw"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): pid, comm, dso, symbol, parent"), - OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, - "Don't shorten the pathnames taking into account the cwd"), OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 030791870e33..8cbea122e349 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -96,8 +96,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->hists_tree = RB_ROOT; self->last_match = NULL; self->mmap_window = 32; - self->cwd = NULL; - self->cwdlen = 0; self->machines = RB_ROOT; self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples_head); @@ -130,7 +128,6 @@ void perf_session__delete(struct perf_session *self) { perf_header__exit(&self->header); close(self->fd); - free(self->cwd); free(self); } @@ -832,23 +829,6 @@ int perf_session__process_events(struct perf_session *self, if (perf_session__register_idle_thread(self) == NULL) return -ENOMEM; - if (!symbol_conf.full_paths) { - char bf[PATH_MAX]; - - if (getcwd(bf, sizeof(bf)) == NULL) { - err = -errno; -out_getcwd_err: - pr_err("failed to get the current directory\n"); - goto out_err; - } - self->cwd = strdup(bf); - if (self->cwd == NULL) { - err = -ENOMEM; - goto out_getcwd_err; - } - self->cwdlen = strlen(self->cwd); - } - if (!self->fd_pipe) err = __perf_session__process_events(self, self->header.data_offset, @@ -856,7 +836,7 @@ out_getcwd_err: self->size, ops); else err = __perf_session__process_pipe_events(self, ops); -out_err: + return err; } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index bb1aab9fa34f..6452a07425a2 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -68,7 +68,6 @@ struct symbol_conf { show_nr_samples, use_callchain, exclude_other, - full_paths, show_cpu_utilization; const char *vmlinux_name, *source_prefix, -- cgit v1.2.3 From 6e406257b3794009e3b7a6d48b54beb547719565 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 29 Jul 2010 15:11:30 -0300 Subject: perf symbols: Precisely specify if dso->{long,short}_name should be freed Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/symbol.c | 5 ++++- tools/perf/util/symbol.h | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 5b81bb29a07a..8151d23664c7 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -456,6 +456,7 @@ static int event__process_kernel_mmap(event_t *self, goto out_problem; map->dso->short_name = name; + map->dso->sname_alloc = 1; map->end = map->start + self->mmap.len; } else if (is_kernel_mmap) { const char *symbol_name = (self->mmap.filename + diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index bc6e7e8c480d..242d2b216f46 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -224,7 +224,9 @@ void dso__delete(struct dso *self) int i; for (i = 0; i < MAP__NR_TYPES; ++i) symbols__delete(&self->symbols[i]); - if (self->long_name != self->name) + if (self->sname_alloc) + free((char *)self->short_name); + if (self->lname_alloc) free(self->long_name); free(self); } @@ -1530,6 +1532,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, if (long_name == NULL) goto failure; dso__set_long_name(map->dso, long_name); + map->dso->lname_alloc = 1; dso__kernel_module_get_build_id(map->dso, ""); } } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 6452a07425a2..f29f73c20809 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -126,12 +126,14 @@ struct dso { struct list_head node; struct rb_root symbols[MAP__NR_TYPES]; struct rb_root symbol_names[MAP__NR_TYPES]; + enum dso_kernel_type kernel; u8 adjust_symbols:1; u8 slen_calculated:1; u8 has_build_id:1; - enum dso_kernel_type kernel; u8 hit:1; u8 annotate_warned:1; + u8 sname_alloc:1; + u8 lname_alloc:1; unsigned char origin; u8 sorted_by_name; u8 loaded; -- cgit v1.2.3 From d65a458b348cd458413b3cfec66e43ebd0367646 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 30 Jul 2010 18:31:28 -0300 Subject: perf tools: Release session and symbol resources on exit So that we reduce the noise when looking for leaks using tools such as valgrind. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 5 ++++- tools/perf/util/event.c | 5 +++-- tools/perf/util/map.c | 26 ++++++++++++++++++++++++++ tools/perf/util/map.h | 1 + tools/perf/util/session.c | 26 ++++++++++++++++++++++++++ tools/perf/util/symbol.c | 9 +++++++++ tools/perf/util/symbol.h | 1 + 7 files changed, 70 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 5ae0d93d8597..ff77b805de71 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -440,6 +440,7 @@ static void atexit_header(void) process_buildids(); perf_header__write(&session->header, output, true); perf_session__delete(session); + symbol__exit(); } } @@ -871,7 +872,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) } else { all_tids=malloc(sizeof(pid_t)); if (!all_tids) - return -ENOMEM; + goto out_symbol_exit; all_tids[0] = target_tid; thread_num = 1; @@ -918,5 +919,7 @@ out_free_fd: } free(all_tids); all_tids = NULL; +out_symbol_exit: + symbol__exit(); return err; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 8151d23664c7..6b0db5577929 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -515,12 +515,13 @@ int event__process_mmap(event_t *self, struct perf_session *session) if (machine == NULL) goto out_problem; thread = perf_session__findnew(session, self->mmap.pid); + if (thread == NULL) + goto out_problem; map = map__new(&machine->user_dsos, self->mmap.start, self->mmap.len, self->mmap.pgoff, self->mmap.pid, self->mmap.filename, MAP__FUNCTION); - - if (thread == NULL || map == NULL) + if (map == NULL) goto out_problem; thread__insert_map(thread, map); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 2ddbae319de5..15d6a6dd50c5 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -539,6 +539,32 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid) return self->root_dir == NULL ? -ENOMEM : 0; } +static void dsos__delete(struct list_head *self) +{ + struct dso *pos, *n; + + list_for_each_entry_safe(pos, n, self, node) { + list_del(&pos->node); + dso__delete(pos); + } +} + +void machine__exit(struct machine *self) +{ + struct kmap *kmap = map__kmap(self->vmlinux_maps[MAP__FUNCTION]); + + if (kmap->ref_reloc_sym) { + free((char *)kmap->ref_reloc_sym->name); + free(kmap->ref_reloc_sym); + } + + map_groups__exit(&self->kmaps); + dsos__delete(&self->user_dsos); + dsos__delete(&self->kernel_dsos); + free(self->root_dir); + self->root_dir = NULL; +} + struct machine *machines__add(struct rb_root *self, pid_t pid, const char *root_dir) { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 20eba420ddae..0e0984e86fce 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -143,6 +143,7 @@ struct machine *machines__find(struct rb_root *self, pid_t pid); struct machine *machines__findnew(struct rb_root *self, pid_t pid); char *machine__mmap_name(struct machine *self, char *bf, size_t size); int machine__init(struct machine *self, const char *root_dir, pid_t pid); +void machine__exit(struct machine *self); /* * Default guest kernel is defined by parameter --guestkallsyms diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8cbea122e349..04a3b3db9e90 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -124,9 +124,35 @@ out_delete: return NULL; } +static void perf_session__delete_dead_threads(struct perf_session *self) +{ + struct thread *n, *t; + + list_for_each_entry_safe(t, n, &self->dead_threads, node) { + list_del(&t->node); + thread__delete(t); + } +} + +static void perf_session__delete_threads(struct perf_session *self) +{ + struct rb_node *nd = rb_first(&self->threads); + + while (nd) { + struct thread *t = rb_entry(nd, struct thread, rb_node); + + rb_erase(&t->rb_node, &self->threads); + nd = rb_next(nd); + thread__delete(t); + } +} + void perf_session__delete(struct perf_session *self) { perf_header__exit(&self->header); + perf_session__delete_dead_threads(self); + perf_session__delete_threads(self); + machine__exit(&self->host_machine); close(self->fd); free(self); } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index d99497ec52aa..94cdf68440cd 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2245,6 +2245,15 @@ out_free_comm_list: return -1; } +void symbol__exit(void) +{ + strlist__delete(symbol_conf.sym_list); + strlist__delete(symbol_conf.dso_list); + strlist__delete(symbol_conf.comm_list); + vmlinux_path__exit(); + symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; +} + int machines__create_kernel_maps(struct rb_root *self, pid_t pid) { struct machine *machine = machines__findnew(self, pid); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index f29f73c20809..33d53ce28958 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -219,6 +219,7 @@ int machines__create_kernel_maps(struct rb_root *self, pid_t pid); int machines__create_guest_kernel_maps(struct rb_root *self); int symbol__init(void); +void symbol__exit(void); bool symbol_type__is_a(char symbol_type, enum map_type map_type); size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp); -- cgit v1.2.3 From 076c6e45215aea0de1ed34d3d5079fabeaabf5e1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Aug 2010 18:18:28 -0300 Subject: perf session: Free the ref_reloc_sym memory at the right place Which is at perf_session__destroy_kernel_maps, counterpart to the perf_session__create_kernel_maps where the kmap structure is located, just after the vmlinux_maps. Make it also check if the kernel maps were actually created, which may not be the case if, for instance, perf_session__new can't complete due to permission problems in, for instance, a 'perf report' case, when a segfault will take place, that is how this was noticed. The problem was introduced in d65a458, thus post .35. This also adds code to release guest machines as them are also created in perf_session__create_kernel_maps, so should be deleted on this newly introduced counterpart, perf_session__destroy_kernel_maps. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/map.c | 18 +++++++++++------- tools/perf/util/map.h | 7 +++++++ tools/perf/util/session.c | 7 +++++++ tools/perf/util/symbol.c | 43 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/symbol.h | 2 ++ 5 files changed, 70 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/symbol.h') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 15d6a6dd50c5..801e6962b0a6 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -506,6 +506,11 @@ void maps__insert(struct rb_root *maps, struct map *map) rb_insert_color(&map->rb_node, maps); } +void maps__remove(struct rb_root *self, struct map *map) +{ + rb_erase(&map->rb_node, self); +} + struct map *maps__find(struct rb_root *maps, u64 ip) { struct rb_node **p = &maps->rb_node; @@ -551,13 +556,6 @@ static void dsos__delete(struct list_head *self) void machine__exit(struct machine *self) { - struct kmap *kmap = map__kmap(self->vmlinux_maps[MAP__FUNCTION]); - - if (kmap->ref_reloc_sym) { - free((char *)kmap->ref_reloc_sym->name); - free(kmap->ref_reloc_sym); - } - map_groups__exit(&self->kmaps); dsos__delete(&self->user_dsos); dsos__delete(&self->kernel_dsos); @@ -565,6 +563,12 @@ void machine__exit(struct machine *self) self->root_dir = NULL; } +void machine__delete(struct machine *self) +{ + machine__exit(self); + free(self); +} + struct machine *machines__add(struct rb_root *self, pid_t pid, const char *root_dir) { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 0e0984e86fce..5b51bbd2f734 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -125,6 +125,7 @@ void map__reloc_vmlinux(struct map *self); size_t __map_groups__fprintf_maps(struct map_groups *self, enum map_type type, int verbose, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); +void maps__remove(struct rb_root *self, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); void map_groups__init(struct map_groups *self); void map_groups__exit(struct map_groups *self); @@ -144,6 +145,7 @@ struct machine *machines__findnew(struct rb_root *self, pid_t pid); char *machine__mmap_name(struct machine *self, char *bf, size_t size); int machine__init(struct machine *self, const char *root_dir, pid_t pid); void machine__exit(struct machine *self); +void machine__delete(struct machine *self); /* * Default guest kernel is defined by parameter --guestkallsyms @@ -165,6 +167,11 @@ static inline void map_groups__insert(struct map_groups *self, struct map *map) map->groups = self; } +static inline void map_groups__remove(struct map_groups *self, struct map *map) +{ + maps__remove(&self->maps[map->type], map); +} + static inline struct map *map_groups__find(struct map_groups *self, enum map_type type, u64 addr) { diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 04a3b3db9e90..5d2fd52fe7b5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -79,6 +79,12 @@ int perf_session__create_kernel_maps(struct perf_session *self) return ret; } +static void perf_session__destroy_kernel_maps(struct perf_session *self) +{ + machine__destroy_kernel_maps(&self->host_machine); + machines__destroy_guest_kernel_maps(&self->machines); +} + struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) { size_t len = filename ? strlen(filename) + 1 : 0; @@ -150,6 +156,7 @@ static void perf_session__delete_threads(struct perf_session *self) void perf_session__delete(struct perf_session *self) { perf_header__exit(&self->header); + perf_session__destroy_kernel_maps(self); perf_session__delete_dead_threads(self); perf_session__delete_threads(self); machine__exit(&self->host_machine); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 3b8c00506672..6f0dd90c36ce 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2107,6 +2107,36 @@ int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) return 0; } +void machine__destroy_kernel_maps(struct machine *self) +{ + enum map_type type; + + for (type = 0; type < MAP__NR_TYPES; ++type) { + struct kmap *kmap; + + if (self->vmlinux_maps[type] == NULL) + continue; + + kmap = map__kmap(self->vmlinux_maps[type]); + map_groups__remove(&self->kmaps, self->vmlinux_maps[type]); + if (kmap->ref_reloc_sym) { + /* + * ref_reloc_sym is shared among all maps, so free just + * on one of them. + */ + if (type == MAP__FUNCTION) { + free((char *)kmap->ref_reloc_sym->name); + kmap->ref_reloc_sym->name = NULL; + free(kmap->ref_reloc_sym); + } + kmap->ref_reloc_sym = NULL; + } + + map__delete(self->vmlinux_maps[type]); + self->vmlinux_maps[type] = NULL; + } +} + int machine__create_kernel_maps(struct machine *self) { struct dso *kernel = machine__create_kernel(self); @@ -2351,6 +2381,19 @@ failure: return ret; } +void machines__destroy_guest_kernel_maps(struct rb_root *self) +{ + struct rb_node *next = rb_first(self); + + while (next) { + struct machine *pos = rb_entry(next, struct machine, rb_node); + + next = rb_next(&pos->rb_node); + rb_erase(&pos->rb_node, self); + machine__delete(pos); + } +} + int machine__load_kallsyms(struct machine *self, const char *filename, enum map_type type, symbol_filter_t filter) { diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 33d53ce28958..906be20011d9 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -212,11 +212,13 @@ int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); +void machine__destroy_kernel_maps(struct machine *self); int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); int machine__create_kernel_maps(struct machine *self); int machines__create_kernel_maps(struct rb_root *self, pid_t pid); int machines__create_guest_kernel_maps(struct rb_root *self); +void machines__destroy_guest_kernel_maps(struct rb_root *self); int symbol__init(void); void symbol__exit(void); -- cgit v1.2.3