From ab255e72204fc5127f0adf40ba103d9335fefa30 Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Thu, 26 Sep 2013 12:36:38 +0100 Subject: perf: parse the .debug_frame section in case .eh_frame is not present On ARM the debug info is not present in the .eh_frame sections but in .debug_frame instead, in dwarf format. Use libunwind to load and parse the debug info. Dependencies: . if present, libunwind >= 1.1 is needed to prevent a segfault when parsing the dwarf info, . libunwind needs to be configured with --enable-debug-frame. Note: --enable-debug-frame is automatically selected on ARM. Acked-by: Jiri Olsa Signed-off-by: Jean Pihet Signed-off-by: Will Deacon --- tools/perf/util/unwind.c | 75 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 16 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c index 2f891f7e70bf..5390d0b8862a 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind.c @@ -39,6 +39,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +extern int +UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); + +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) + #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ @@ -245,8 +254,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, return 0; } -static int read_unwind_spec(struct dso *dso, struct machine *machine, - u64 *table_data, u64 *segbase, u64 *fde_count) +static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, + u64 *table_data, u64 *segbase, + u64 *fde_count) { int ret = -EINVAL, fd; u64 offset; @@ -255,6 +265,7 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine, if (fd < 0) return -EINVAL; + /* Check the .eh_frame section for unwinding info */ offset = elf_section_offset(fd, ".eh_frame_hdr"); close(fd); @@ -263,10 +274,29 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine, table_data, segbase, fde_count); - /* TODO .debug_frame check if eh_frame_hdr fails */ return ret; } +#ifndef NO_LIBUNWIND_DEBUG_FRAME +static int read_unwind_spec_debug_frame(struct dso *dso, + struct machine *machine, u64 *offset) +{ + int fd = dso__data_fd(dso, machine); + + if (fd < 0) + return -EINVAL; + + /* Check the .debug_frame section for unwinding info */ + *offset = elf_section_offset(fd, ".debug_frame"); + close(fd); + + if (*offset) + return 0; + + return -EINVAL; +} +#endif + static struct map *find_map(unw_word_t ip, struct unwind_info *ui) { struct addr_location al; @@ -291,20 +321,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); - if (read_unwind_spec(map->dso, ui->machine, - &table_data, &segbase, &fde_count)) - return -EINVAL; + /* Check the .eh_frame section for unwinding info */ + if (!read_unwind_spec_eh_frame(map->dso, ui->machine, + &table_data, &segbase, &fde_count)) { + memset(&di, 0, sizeof(di)); + di.format = UNW_INFO_FORMAT_REMOTE_TABLE; + di.start_ip = map->start; + di.end_ip = map->end; + di.u.rti.segbase = map->start + segbase; + di.u.rti.table_data = map->start + table_data; + di.u.rti.table_len = fde_count * sizeof(struct table_entry) + / sizeof(unw_word_t); + return dwarf_search_unwind_table(as, ip, &di, pi, + need_unwind_info, arg); + } + +#ifndef NO_LIBUNWIND_DEBUG_FRAME + /* Check the .debug_frame section for unwinding info */ + if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { + memset(&di, 0, sizeof(di)); + dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name, + map->start, map->end); + return dwarf_search_unwind_table(as, ip, &di, pi, + need_unwind_info, arg); + } +#endif - memset(&di, 0, sizeof(di)); - di.format = UNW_INFO_FORMAT_REMOTE_TABLE; - di.start_ip = map->start; - di.end_ip = map->end; - di.u.rti.segbase = map->start + segbase; - di.u.rti.table_data = map->start + table_data; - di.u.rti.table_len = fde_count * sizeof(struct table_entry) - / sizeof(unw_word_t); - return dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); + return -EINVAL; } static int access_fpreg(unw_addr_space_t __maybe_unused as, -- cgit v1.2.2 From 354cc40e3b0981c38ba2ce2964954480e6c03c37 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Oct 2013 07:22:15 -0700 Subject: tools/perf: Fix sorting for 64bit entries Some of the node comparisons in hist.c dropped the upper 32bit by using an int variable to store the compare result. This broke various 64bit fields, causing incorrect collapsing (found for the TSX transaction field) Just use int64_t always. Acked-by: Namhyung Kim Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1380637335-30110-1-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- tools/perf/util/hist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9ff6cf3e9a99..97dc2808885d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -346,7 +346,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, struct rb_node **p; struct rb_node *parent = NULL; struct hist_entry *he; - int cmp; + int64_t cmp; p = &hists->entries_in->rb_node; @@ -884,7 +884,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, struct rb_node **p; struct rb_node *parent = NULL; struct hist_entry *he; - int cmp; + int64_t cmp; if (sort__need_collapse) root = &hists->entries_collapsed; -- cgit v1.2.2 From 4cabc3d1cb6a46f581a2628d1d11c483d5f300e5 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 21 Aug 2013 16:47:26 -0700 Subject: tools/perf/stat: Add perf stat --transaction Add support to perf stat to print the basic transactional execution statistics: Total cycles, Cycles in Transaction, Cycles in aborted transsactions using the in_tx and in_tx_checkpoint qualifiers. Transaction Starts and Elision Starts, to compute the average transaction length. This is a reasonable overview over the success of the transactions. Also support architectures that have a transaction aborted cycles counter like POWER8. Since that is awkward to handle in the kernel abstract handle both cases here. Enable with a new --transaction / -T option. This requires measuring these events in a group, since they depend on each other. This is implemented by using TM sysfs events exported by the kernel Signed-off-by: Andi Kleen Acked-by: Arnaldo Carvalho de Melo Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1377128846-977-5-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- tools/perf/util/evsel.h | 6 ++++++ tools/perf/util/pmu.c | 16 ++++++++++++++++ tools/perf/util/pmu.h | 1 + 3 files changed, 23 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4a7bdc713bab..5aa68cddc7d9 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -197,6 +197,12 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1, (e1->attr.config == e2->attr.config); } +#define perf_evsel__cmp(a, b) \ + ((a) && \ + (b) && \ + (a)->attr.type == (b)->attr.type && \ + (a)->attr.config == (b)->attr.config) + int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, int cpu, int thread, bool scale); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index bc9d8069d376..64362fe45b71 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -637,3 +637,19 @@ void print_pmu_events(const char *event_glob, bool name_only) printf("\n"); free(aliases); } + +bool pmu_have_event(const char *pname, const char *name) +{ + struct perf_pmu *pmu; + struct perf_pmu_alias *alias; + + pmu = NULL; + while ((pmu = perf_pmu__scan(pmu)) != NULL) { + if (strcmp(pname, pmu->name)) + continue; + list_for_each_entry(alias, &pmu->aliases, list) + if (!strcmp(alias->name, name)) + return true; + } + return false; +} diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 6b2cbe2d4cc3..1179b26f244a 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -42,6 +42,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); void print_pmu_events(const char *event_glob, bool name_only); +bool pmu_have_event(const char *pname, const char *name); int perf_pmu__test(void); #endif /* __PMU_H */ -- cgit v1.2.2 From f5d05bcec409aec2c41727077ad818f7c4db005b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 20 Sep 2013 07:40:41 -0700 Subject: tools/perf: Support sorting by in_tx or abort branch flags Extend the perf branch sorting code to support sorting by in_tx or abort_tx qualifiers. Also print out those qualifiers. This also fixes up some of the existing sort key documentation. We do not support no_tx here, because it's simply not showing the in_tx flag. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379688044-14173-4-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- tools/perf/util/hist.h | 2 ++ tools/perf/util/sort.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/sort.h | 2 ++ 3 files changed, 55 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1329b6b6ffe6..f743e96005f2 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -45,6 +45,8 @@ enum hist_column { HISTC_CPU, HISTC_SRCLINE, HISTC_MISPREDICT, + HISTC_IN_TX, + HISTC_ABORT, HISTC_SYMBOL_FROM, HISTC_SYMBOL_TO, HISTC_DSO_FROM, diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 5f118a089519..1771566858cc 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -858,6 +858,55 @@ struct sort_entry sort_mem_snoop = { .se_width_idx = HISTC_MEM_SNOOP, }; +static int64_t +sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return left->branch_info->flags.abort != + right->branch_info->flags.abort; +} + +static int hist_entry__abort_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + static const char *out = "."; + + if (self->branch_info->flags.abort) + out = "A"; + return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_abort = { + .se_header = "Transaction abort", + .se_cmp = sort__abort_cmp, + .se_snprintf = hist_entry__abort_snprintf, + .se_width_idx = HISTC_ABORT, +}; + +static int64_t +sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return left->branch_info->flags.in_tx != + right->branch_info->flags.in_tx; +} + +static int hist_entry__in_tx_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + static const char *out = "."; + + if (self->branch_info->flags.in_tx) + out = "T"; + + return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_in_tx = { + .se_header = "Branch in transaction", + .se_cmp = sort__in_tx_cmp, + .se_snprintf = hist_entry__in_tx_snprintf, + .se_width_idx = HISTC_IN_TX, +}; + struct sort_dimension { const char *name; struct sort_entry *entry; @@ -888,6 +937,8 @@ static struct sort_dimension bstack_sort_dimensions[] = { DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), + DIM(SORT_IN_TX, "in_tx", sort_in_tx), + DIM(SORT_ABORT, "abort", sort_abort), }; #undef DIM diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 4e80dbd271e7..9dad3a0440dc 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -153,6 +153,8 @@ enum sort_type { SORT_SYM_FROM, SORT_SYM_TO, SORT_MISPREDICT, + SORT_ABORT, + SORT_IN_TX, /* memory mode specific sort keys */ __SORT_MEMORY_MODE, -- cgit v1.2.2 From 475eeab9f3c1579c8da89667496084db4867bf7c Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 20 Sep 2013 07:40:43 -0700 Subject: tools/perf: Add support for record transaction flags Add support for recording and displaying the transaction flags. They are essentially a new sort key. Also display them in a nice way to the user. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1379688044-14173-6-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- tools/perf/util/event.h | 1 + tools/perf/util/evsel.c | 9 ++++++ tools/perf/util/hist.c | 7 ++++- tools/perf/util/hist.h | 4 ++- tools/perf/util/session.c | 3 ++ tools/perf/util/sort.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/sort.h | 2 ++ 7 files changed, 97 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c67ecc457d29..17d9e167a7b9 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -111,6 +111,7 @@ struct perf_sample { u64 stream_id; u64 period; u64 weight; + u64 transaction; u32 cpu; u32 raw_size; u64 data_src; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0ce9febf1ba0..abe69af58b62 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -681,6 +681,9 @@ void perf_evsel__config(struct perf_evsel *evsel, attr->mmap2 = track && !perf_missing_features.mmap2; attr->comm = track; + if (opts->sample_transaction) + attr->sample_type |= PERF_SAMPLE_TRANSACTION; + /* * XXX see the function comment above * @@ -1470,6 +1473,12 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, array++; } + data->transaction = 0; + if (type & PERF_SAMPLE_TRANSACTION) { + data->transaction = *array; + array++; + } + return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 97dc2808885d..f3278a388e9a 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -160,6 +160,10 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); + + if (h->transaction) + hists__new_col_len(hists, HISTC_TRANSACTION, + hist_entry__transaction_len()); } void hists__output_recalc_col_len(struct hists *hists, int max_rows) @@ -466,7 +470,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, struct symbol *sym_parent, u64 period, - u64 weight) + u64 weight, u64 transaction) { struct hist_entry entry = { .thread = al->thread, @@ -487,6 +491,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, .hists = self, .branch_info = NULL, .mem_info = NULL, + .transaction = transaction, }; return add_hist_entry(self, &entry, al, period, weight); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index f743e96005f2..6a048c09cd64 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -59,6 +59,7 @@ enum hist_column { HISTC_MEM_TLB, HISTC_MEM_LVL, HISTC_MEM_SNOOP, + HISTC_TRANSACTION, HISTC_NR_COLS, /* Last entry */ }; @@ -84,9 +85,10 @@ struct hists { struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, struct symbol *parent, u64 period, - u64 weight); + u64 weight, u64 transaction); int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); +int hist_entry__transaction_len(void); int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, struct hists *hists); void hist_entry__free(struct hist_entry *); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 70ffa41518f3..211b325791ad 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -858,6 +858,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, if (sample_type & PERF_SAMPLE_DATA_SRC) printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); + if (sample_type & PERF_SAMPLE_TRANSACTION) + printf("... transaction: %" PRIx64 "\n", sample->transaction); + if (sample_type & PERF_SAMPLE_READ) sample_read__printf(sample, evsel->attr.read_format); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1771566858cc..b4ecc0e4c908 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -907,6 +907,78 @@ struct sort_entry sort_in_tx = { .se_width_idx = HISTC_IN_TX, }; +static int64_t +sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return left->transaction - right->transaction; +} + +static inline char *add_str(char *p, const char *str) +{ + strcpy(p, str); + return p + strlen(str); +} + +static struct txbit { + unsigned flag; + const char *name; + int skip_for_len; +} txbits[] = { + { PERF_TXN_ELISION, "EL ", 0 }, + { PERF_TXN_TRANSACTION, "TX ", 1 }, + { PERF_TXN_SYNC, "SYNC ", 1 }, + { PERF_TXN_ASYNC, "ASYNC ", 0 }, + { PERF_TXN_RETRY, "RETRY ", 0 }, + { PERF_TXN_CONFLICT, "CON ", 0 }, + { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, + { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 }, + { 0, NULL, 0 } +}; + +int hist_entry__transaction_len(void) +{ + int i; + int len = 0; + + for (i = 0; txbits[i].name; i++) { + if (!txbits[i].skip_for_len) + len += strlen(txbits[i].name); + } + len += 4; /* :XX */ + return len; +} + +static int hist_entry__transaction_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) +{ + u64 t = self->transaction; + char buf[128]; + char *p = buf; + int i; + + buf[0] = 0; + for (i = 0; txbits[i].name; i++) + if (txbits[i].flag & t) + p = add_str(p, txbits[i].name); + if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) + p = add_str(p, "NEITHER "); + if (t & PERF_TXN_ABORT_MASK) { + sprintf(p, ":%" PRIx64, + (t & PERF_TXN_ABORT_MASK) >> + PERF_TXN_ABORT_SHIFT); + p += strlen(p); + } + + return repsep_snprintf(bf, size, "%-*s", width, buf); +} + +struct sort_entry sort_transaction = { + .se_header = "Transaction ", + .se_cmp = sort__transaction_cmp, + .se_snprintf = hist_entry__transaction_snprintf, + .se_width_idx = HISTC_TRANSACTION, +}; + struct sort_dimension { const char *name; struct sort_entry *entry; @@ -925,6 +997,7 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_SRCLINE, "srcline", sort_srcline), DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), + DIM(SORT_TRANSACTION, "transaction", sort_transaction), }; #undef DIM diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 9dad3a0440dc..bf4333694d3a 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -85,6 +85,7 @@ struct hist_entry { struct map_symbol ms; struct thread *thread; u64 ip; + u64 transaction; s32 cpu; struct hist_entry_diff diff; @@ -145,6 +146,7 @@ enum sort_type { SORT_SRCLINE, SORT_LOCAL_WEIGHT, SORT_GLOBAL_WEIGHT, + SORT_TRANSACTION, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, -- cgit v1.2.2 From 89fe808ae777728da6e1d78b7d13562792310d17 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 30 Sep 2013 12:07:11 +0200 Subject: tools/perf: Standardize feature support define names to: HAVE_{FEATURE}_SUPPORT Standardize all the feature flags based on the HAVE_{FEATURE}_SUPPORT naming convention: HAVE_ARCH_X86_64_SUPPORT HAVE_BACKTRACE_SUPPORT HAVE_CPLUS_DEMANGLE_SUPPORT HAVE_DWARF_SUPPORT HAVE_ELF_GETPHDRNUM_SUPPORT HAVE_GTK2_SUPPORT HAVE_GTK_INFO_BAR_SUPPORT HAVE_LIBAUDIT_SUPPORT HAVE_LIBELF_MMAP_SUPPORT HAVE_LIBELF_SUPPORT HAVE_LIBNUMA_SUPPORT HAVE_LIBUNWIND_SUPPORT HAVE_ON_EXIT_SUPPORT HAVE_PERF_REGS_SUPPORT HAVE_SLANG_SUPPORT HAVE_STRLCPY_SUPPORT Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-u3zvqejddfZhtrbYbfhi3spa@git.kernel.org Signed-off-by: Ingo Molnar --- tools/perf/util/annotate.h | 4 ++-- tools/perf/util/cache.h | 2 +- tools/perf/util/generate-cmdlist.sh | 4 ++-- tools/perf/util/hist.h | 4 ++-- tools/perf/util/include/dwarf-regs.h | 2 +- tools/perf/util/map.c | 2 +- tools/perf/util/path.c | 2 +- tools/perf/util/perf_regs.h | 4 ++-- tools/perf/util/probe-event.c | 4 ++-- tools/perf/util/probe-finder.h | 4 ++-- tools/perf/util/symbol-elf.c | 2 +- tools/perf/util/symbol.h | 8 ++++---- tools/perf/util/unwind.h | 4 ++-- tools/perf/util/util.c | 4 ++-- 14 files changed, 25 insertions(+), 25 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index af755156d278..f0699e9bcc6f 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -150,7 +150,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, bool print_lines, bool full_paths, int min_pcnt, int max_lines); -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT int symbol__tui_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, struct hist_browser_timer *hbt); @@ -165,7 +165,7 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, } #endif -#ifdef GTK2_SUPPORT +#ifdef HAVE_GTK2_SUPPORT int symbol__gtk_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, struct hist_browser_timer *hbt); diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 26e367239873..442953c1ce85 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -70,7 +70,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2 extern char *perf_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -#ifndef HAVE_STRLCPY +#ifndef HAVE_STRLCPY_SUPPORT extern size_t strlcpy(char *dest, const char *src, size_t size); #endif diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index 3ac38031d534..36a885d2cd22 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh @@ -22,7 +22,7 @@ do }' "Documentation/perf-$cmd.txt" done -echo "#ifdef LIBELF_SUPPORT" +echo "#ifdef HAVE_LIBELF_SUPPORT" sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt | sort | while read cmd @@ -35,5 +35,5 @@ do p }' "Documentation/perf-$cmd.txt" done -echo "#endif /* LIBELF_SUPPORT */" +echo "#endif /* HAVE_LIBELF_SUPPORT */" echo "};" diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 6a048c09cd64..ed4f90ebe0d5 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -187,7 +187,7 @@ struct hist_browser_timer { int refresh; }; -#ifdef SLANG_SUPPORT +#ifdef HAVE_SLANG_SUPPORT #include "../ui/keysyms.h" int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, struct hist_browser_timer *hbt); @@ -228,7 +228,7 @@ static inline int script_browse(const char *script_opt __maybe_unused) #define K_SWITCH_INPUT_DATA -3000 #endif -#ifdef GTK2_SUPPORT +#ifdef HAVE_GTK2_SUPPORT int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, struct hist_browser_timer *hbt __maybe_unused, float min_pcnt); diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h index cf6727e99c44..8f149655f497 100644 --- a/tools/perf/util/include/dwarf-regs.h +++ b/tools/perf/util/include/dwarf-regs.h @@ -1,7 +1,7 @@ #ifndef _PERF_DWARF_REGS_H_ #define _PERF_DWARF_REGS_H_ -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT const char *get_arch_regstr(unsigned int n); #endif diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4f6680d2043b..17ee458a0870 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -172,7 +172,7 @@ int map__load(struct map *map, symbol_filter_t filter) pr_warning(", continuing without symbols\n"); return -1; } else if (nr == 0) { -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT const size_t len = strlen(name); const size_t real_len = len - sizeof(DSO__DELETED); diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index a8c49548ca48..f3958743b743 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -22,7 +22,7 @@ static const char *get_perf_dir(void) return "."; } -#ifndef HAVE_STRLCPY +#ifndef HAVE_STRLCPY_SUPPORT size_t strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 5a4f2b6f3738..a3d42cd74919 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -1,7 +1,7 @@ #ifndef __PERF_REGS_H #define __PERF_REGS_H -#ifdef HAVE_PERF_REGS +#ifdef HAVE_PERF_REGS_SUPPORT #include #else #define PERF_REGS_MASK 0 @@ -10,5 +10,5 @@ static inline const char *perf_reg_name(int id __maybe_unused) { return NULL; } -#endif /* HAVE_PERF_REGS */ +#endif /* HAVE_PERF_REGS_SUPPORT */ #endif /* __PERF_REGS_H */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index aa04bf9c9ad7..779b2dacd43f 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -201,7 +201,7 @@ static int convert_to_perf_probe_point(struct probe_trace_point *tp, return 0; } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT /* Open new debuginfo of given module */ static struct debuginfo *open_debuginfo(const char *module) { @@ -630,7 +630,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, return ret; } -#else /* !DWARF_SUPPORT */ +#else /* !HAVE_DWARF_SUPPORT */ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, struct perf_probe_point *pp) diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 3b7d63018960..3f0c29dd6ac5 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -14,7 +14,7 @@ static inline int is_c_varname(const char *name) return isalpha(name[0]) || name[0] == '_'; } -#ifdef DWARF_SUPPORT +#ifdef HAVE_DWARF_SUPPORT #include "dwarf-aux.h" @@ -105,6 +105,6 @@ struct line_finder { int found; }; -#endif /* DWARF_SUPPORT */ +#endif /* HAVE_DWARF_SUPPORT */ #endif /*_PROBE_FINDER_H */ diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index a9c829be5216..c37693052800 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -8,7 +8,7 @@ #include "symbol.h" #include "debug.h" -#ifndef HAVE_ELF_GETPHDRNUM +#ifndef HAVE_ELF_GETPHDRNUM_SUPPORT static int elf_getphdrnum(Elf *elf, size_t *dst) { GElf_Ehdr gehdr; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index fd5b70ea2981..2a97bb1a8097 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -13,7 +13,7 @@ #include #include "build-id.h" -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT #include #include #endif @@ -21,7 +21,7 @@ #include "dso.h" -#ifdef HAVE_CPLUS_DEMANGLE +#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT extern char *cplus_demangle(const char *, int); static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) @@ -46,7 +46,7 @@ static inline char *bfd_demangle(void __maybe_unused *v, * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; * for newer versions we can use mmap to reduce memory usage: */ -#ifdef LIBELF_MMAP +#ifdef HAVE_LIBELF_MMAP_SUPPORT # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP #else # define PERF_ELF_C_READ_MMAP ELF_C_READ @@ -178,7 +178,7 @@ struct symsrc { int fd; enum dso_binary_type type; -#ifdef LIBELF_SUPPORT +#ifdef HAVE_LIBELF_SUPPORT Elf *elf; GElf_Ehdr ehdr; diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index cb6bc503a792..ec0c71a2ca2e 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -13,7 +13,7 @@ struct unwind_entry { typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); -#ifdef LIBUNWIND_SUPPORT +#ifdef HAVE_LIBUNWIND_SUPPORT int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct machine *machine, struct thread *thread, @@ -31,5 +31,5 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, { return 0; } -#endif /* LIBUNWIND_SUPPORT */ +#endif /* HAVE_LIBUNWIND_SUPPORT */ #endif /* __UNWIND_H */ diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 6d17b18e915d..ccfdeb62f576 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -1,7 +1,7 @@ #include "../perf.h" #include "util.h" #include -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT #include #endif #include @@ -204,7 +204,7 @@ int hex2u64(const char *ptr, u64 *long_val) } /* Obtain a backtrace and print it to stdout. */ -#ifdef BACKTRACE_SUPPORT +#ifdef HAVE_BACKTRACE_SUPPORT void dump_stack(void) { void *array[16]; -- cgit v1.2.2 From 7a10822a30060eceddda16c051086c00acb449c9 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 7 Oct 2013 11:26:18 +0200 Subject: tools/perf: Clean up util/include/linux/compiler.h Use the standard CPP style we use in the kernel: #ifndef foo # define foo bar #endif Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-iqyVrrHqpn0eiwenvgwrh8lf@git.kernel.org Signed-off-by: Ingo Molnar --- tools/perf/util/include/linux/compiler.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index 96b919dae11c..bef4d3ddc493 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h @@ -2,20 +2,25 @@ #define _PERF_LINUX_COMPILER_H_ #ifndef __always_inline -#define __always_inline inline +# define __always_inline inline __attribute__((always_inline)) #endif + #define __user + #ifndef __attribute_const__ -#define __attribute_const__ +# define __attribute_const__ #endif #ifndef __maybe_unused -#define __maybe_unused __attribute__((unused)) +# define __maybe_unused __attribute__((unused)) +#endif + +#ifndef __packed +# define __packed __attribute__((__packed__)) #endif -#define __packed __attribute__((__packed__)) #ifndef __force -#define __force +# define __force #endif #endif -- cgit v1.2.2 From fb1c9185e36cf9c616ac15f54e54a01f052672bd Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 1 Oct 2013 13:26:13 +0200 Subject: tools/perf: Turn strlcpy() into a __weak function The strlcpy() feature check slows every build unnecessarily - so make it a __weak function so it does not have to be auto-detected. If the libc (or any other library) has an strlcpy() implementation it will be used - otherwise our fallback is active. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/n/tip-zjbrcupapu08ePsyYhhhxiwk@git.kernel.org Signed-off-by: Ingo Molnar --- tools/perf/util/cache.h | 3 +-- tools/perf/util/include/linux/compiler.h | 4 ++++ tools/perf/util/path.c | 10 +++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 442953c1ce85..7b176dd02e1a 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -70,8 +70,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2 extern char *perf_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -#ifndef HAVE_STRLCPY_SUPPORT +/* Matches the libc/libbsd function attribute so we declare this unconditionally: */ extern size_t strlcpy(char *dest, const char *src, size_t size); -#endif #endif /* __PERF_CACHE_H */ diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index bef4d3ddc493..b003ad7200b2 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h @@ -23,4 +23,8 @@ # define __force #endif +#ifndef __weak +# define __weak __attribute__((weak)) +#endif + #endif diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index f3958743b743..5d13cb45b317 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -22,19 +22,23 @@ static const char *get_perf_dir(void) return "."; } -#ifndef HAVE_STRLCPY_SUPPORT -size_t strlcpy(char *dest, const char *src, size_t size) +/* + * If libc has strlcpy() then that version will override this + * implementation: + */ +size_t __weak strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); if (size) { size_t len = (ret >= size) ? size - 1 : ret; + memcpy(dest, src, len); dest[len] = '\0'; } + return ret; } -#endif static char *get_pathname(void) { -- cgit v1.2.2 From 9cd00941f8c274e6ca03ed238d96ddb0be474b86 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 18 Sep 2013 15:56:14 +0200 Subject: perf symbols: Support for Openembedded/Yocto -dbg packages On OpenEmbedded the symbol files are located under a .debug folder on the same folder as the binary file. This patch adds support for such files. Without this patch on perf top you can see: no symbols found in /usr/lib/gstreamer-1.0/libtheoraenc.so.1.1.2, maybe install a debug package? 84.56% libtheoraenc.so.1.1.2 [.] 0x000000000000b346 With this patch symbols are shown: 19.06% libtheoraenc.so.1.1.2 [.] oc_int_frag_satd_thresh_mmxext 9.76% libtheoraenc.so.1.1.2 [.] oc_analyze_mb_mode_luma 5.58% libtheoraenc.so.1.1.2 [.] oc_qii_state_advance 4.84% libtheoraenc.so.1.1.2 [.] oc_enc_tokenize_ac ... Signed-off-by: Ricardo Ribalda Delgado Acked-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Waiman Long Link: http://lkml.kernel.org/r/1379512574-25912-1-git-send-email-ricardo.ribalda@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 49 +++++++++++++++++++++++++++++++++++------------- tools/perf/util/dso.h | 1 + tools/perf/util/symbol.c | 1 + 3 files changed, 38 insertions(+), 13 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index e3c1ff8512c8..6bfc8aacaf7c 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -7,19 +7,20 @@ char dso__symtab_origin(const struct dso *dso) { static const char origin[] = { - [DSO_BINARY_TYPE__KALLSYMS] = 'k', - [DSO_BINARY_TYPE__VMLINUX] = 'v', - [DSO_BINARY_TYPE__JAVA_JIT] = 'j', - [DSO_BINARY_TYPE__DEBUGLINK] = 'l', - [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', - [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', - [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', - [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', - [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', - [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', - [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', - [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', - [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', + [DSO_BINARY_TYPE__KALLSYMS] = 'k', + [DSO_BINARY_TYPE__VMLINUX] = 'v', + [DSO_BINARY_TYPE__JAVA_JIT] = 'j', + [DSO_BINARY_TYPE__DEBUGLINK] = 'l', + [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', + [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', + [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', + [DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO] = 'o', + [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', + [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', + [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', + [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', + [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', + [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', }; if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) @@ -64,6 +65,28 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, symbol_conf.symfs, dso->long_name); break; + case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: + { + char *last_slash; + size_t len; + size_t dir_size; + + last_slash = dso->long_name + dso->long_name_len; + while (last_slash != dso->long_name && *last_slash != '/') + last_slash--; + + len = scnprintf(file, size, "%s", symbol_conf.symfs); + dir_size = last_slash - dso->long_name + 2; + if (dir_size > (size - len)) { + ret = -1; + break; + } + len += scnprintf(file + len, dir_size, "%s", dso->long_name); + len += scnprintf(file + len , size - len, ".debug%s", + last_slash); + break; + } + case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: if (!dso->has_build_id) { ret = -1; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index b793053335d6..dbd9241ea290 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -23,6 +23,7 @@ enum dso_binary_type { DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, DSO_BINARY_TYPE__KCORE, DSO_BINARY_TYPE__GUEST_KCORE, + DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, DSO_BINARY_TYPE__NOT_FOUND, }; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7eb0362f4ffd..cd1dcc45049b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -51,6 +51,7 @@ static enum dso_binary_type binary_type_symtab[] = { DSO_BINARY_TYPE__SYSTEM_PATH_DSO, DSO_BINARY_TYPE__GUEST_KMODULE, DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, + DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, DSO_BINARY_TYPE__NOT_FOUND, }; -- cgit v1.2.2 From 5283ec23a02e8afdc984c7f5f07e2a2662d4934a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Sep 2013 18:39:34 +0200 Subject: perf tools: Remove unused trace-event-* code Removing unused trace-event-* code. Acked-by: Namhyung Kim Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1379003976-5839-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-parse.c | 36 ------------------------------------ tools/perf/util/trace-event.h | 9 --------- 2 files changed, 45 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index e9e1c03f927d..6681f71f2f95 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -120,42 +120,6 @@ raw_field_value(struct event_format *event, const char *name, void *data) return val; } -void *raw_field_ptr(struct event_format *event, const char *name, void *data) -{ - struct format_field *field; - - field = pevent_find_any_field(event, name); - if (!field) - return NULL; - - if (field->flags & FIELD_IS_DYNAMIC) { - int offset; - - offset = *(int *)(data + field->offset); - offset &= 0xffff; - - return data + offset; - } - - return data + field->offset; -} - -int trace_parse_common_type(struct pevent *pevent, void *data) -{ - struct pevent_record record; - - record.data = data; - return pevent_data_type(pevent, &record); -} - -int trace_parse_common_pid(struct pevent *pevent, void *data) -{ - struct pevent_record record; - - record.data = data; - return pevent_data_pid(pevent, &record); -} - unsigned long long read_size(struct event_format *event, void *ptr, int size) { return pevent_read_number(event->pevent, ptr, size); diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index fafe1a40444a..04df63114109 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -11,8 +11,6 @@ union perf_event; struct perf_tool; struct thread; -extern struct pevent *perf_pevent; - int bigendian(void); struct pevent *read_trace_init(int file_bigendian, int host_bigendian); @@ -23,26 +21,19 @@ int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); int parse_event_file(struct pevent *pevent, char *buf, unsigned long size, char *sys); -struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu); - unsigned long long raw_field_value(struct event_format *event, const char *name, void *data); -void *raw_field_ptr(struct event_format *event, const char *name, void *data); void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); ssize_t trace_report(int fd, struct pevent **pevent, bool repipe); -int trace_parse_common_type(struct pevent *pevent, void *data); -int trace_parse_common_pid(struct pevent *pevent, void *data); - struct event_format *trace_find_next_event(struct pevent *pevent, struct event_format *event); unsigned long long read_size(struct event_format *event, void *ptr, int size); unsigned long long eval_flag(const char *flag); -struct pevent_record *trace_read_data(struct pevent *pevent, int cpu); int read_tracing_data(int fd, struct list_head *pattrs); struct tracing_data { -- cgit v1.2.2 From 918512b435e15fefe609d236e0ecd62cb8f389c9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Sep 2013 18:39:35 +0200 Subject: perf tools: Unify page_size usage Making page_size global from the util object. Removing the not needed one. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1379003976-5839-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/python.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 71b5412bbbb9..a24ce0a6a941 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1036,6 +1036,7 @@ PyMODINIT_FUNC initperf(void) pyrf_cpu_map__setup_types() < 0) return; + /* The page_size is placed in util object. */ page_size = sysconf(_SC_PAGE_SIZE); Py_INCREF(&pyrf_evlist__type); -- cgit v1.2.2 From 994a1f78b191df0c9d6caca3f3afb03e247aff26 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 1 Sep 2013 12:36:12 +0200 Subject: perf tools: Check mmap pages value early Move the check of the mmap_pages value to the options parsing time, so we could rely on this value on other parts of code. Related changes come in the next patches. Also changes perf_evlist::mmap_len to proper size_t type. Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378031796-17892-2-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 46 +++++++++++++++++++++++++++++++++++++--------- tools/perf/util/evlist.h | 6 +++++- 2 files changed, 42 insertions(+), 10 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f9f77bee0b1b..4283f49ca0ab 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -18,6 +18,7 @@ #include #include "parse-events.h" +#include "parse-options.h" #include @@ -672,6 +673,40 @@ out_unmap: return -1; } +static size_t perf_evlist__mmap_size(unsigned long pages) +{ + /* 512 kiB: default amount of unprivileged mlocked memory */ + if (pages == UINT_MAX) + pages = (512 * 1024) / page_size; + else if (!is_power_of_2(pages)) + return 0; + + return (pages + 1) * page_size; +} + +int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, + int unset __maybe_unused) +{ + unsigned int pages, *mmap_pages = opt->value; + size_t size; + char *eptr; + + pages = strtoul(str, &eptr, 10); + if (*eptr != '\0') { + pr_err("failed to parse --mmap_pages/-m value\n"); + return -1; + } + + size = perf_evlist__mmap_size(pages); + if (!size) { + pr_err("--mmap_pages/-m value must be a power of two."); + return -1; + } + + *mmap_pages = pages; + return 0; +} + /** perf_evlist__mmap - Create per cpu maps to receive events * * @evlist - list of events @@ -695,14 +730,6 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, const struct thread_map *threads = evlist->threads; int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask; - /* 512 kiB: default amount of unprivileged mlocked memory */ - if (pages == UINT_MAX) - pages = (512 * 1024) / page_size; - else if (!is_power_of_2(pages)) - return -EINVAL; - - mask = pages * page_size - 1; - if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) return -ENOMEM; @@ -710,7 +737,8 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, return -ENOMEM; evlist->overwrite = overwrite; - evlist->mmap_len = (pages + 1) * page_size; + evlist->mmap_len = perf_evlist__mmap_size(pages); + mask = evlist->mmap_len - page_size - 1; list_for_each_entry(evsel, &evlist->entries, node) { if ((evsel->attr.read_format & PERF_FORMAT_ID) && diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 880d7139d2fb..4edb5008fd36 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -31,7 +31,7 @@ struct perf_evlist { int nr_groups; int nr_fds; int nr_mmaps; - int mmap_len; + size_t mmap_len; int id_pos; int is_pos; u64 combined_sample_type; @@ -103,6 +103,10 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, bool want_signal); int perf_evlist__start_workload(struct perf_evlist *evlist); +int perf_evlist__parse_mmap_pages(const struct option *opt, + const char *str, + int unset); + int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, bool overwrite); void perf_evlist__munmap(struct perf_evlist *evlist); -- cgit v1.2.2 From 27050f530dc4fd88dc93d85c177e000efe970d12 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 1 Sep 2013 12:36:13 +0200 Subject: perf tools: Add possibility to specify mmap size Adding possibility to specify mmap size via -m/--mmap-pages by appending unit size character (B/K/M/G) to the number, like: $ perf record -m 8K ls $ perf record -m 2M ls The size is rounded up appropriately to follow perf mmap restrictions. If no unit is specified the number provides pages as of now, like: $ perf record -m 8 ls Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378031796-17892-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 32 ++++++++++++++++++++++++++------ tools/perf/util/util.c | 25 +++++++++++++++++++++++++ tools/perf/util/util.h | 14 ++++++++++++++ 3 files changed, 65 insertions(+), 6 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 4283f49ca0ab..d21ab0812926 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -687,14 +687,33 @@ static size_t perf_evlist__mmap_size(unsigned long pages) int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, int unset __maybe_unused) { - unsigned int pages, *mmap_pages = opt->value; + unsigned int pages, val, *mmap_pages = opt->value; size_t size; - char *eptr; + static struct parse_tag tags[] = { + { .tag = 'B', .mult = 1 }, + { .tag = 'K', .mult = 1 << 10 }, + { .tag = 'M', .mult = 1 << 20 }, + { .tag = 'G', .mult = 1 << 30 }, + { .tag = 0 }, + }; - pages = strtoul(str, &eptr, 10); - if (*eptr != '\0') { - pr_err("failed to parse --mmap_pages/-m value\n"); - return -1; + val = parse_tag_value(str, tags); + if (val != (unsigned int) -1) { + /* we got file size value */ + pages = PERF_ALIGN(val, page_size) / page_size; + if (!is_power_of_2(pages)) { + pages = next_pow2(pages); + pr_info("rounding mmap pages size to %u (%u pages)\n", + pages * page_size, pages); + } + } else { + /* we got pages count value */ + char *eptr; + pages = strtoul(str, &eptr, 10); + if (*eptr != '\0') { + pr_err("failed to parse --mmap_pages/-m value\n"); + return -1; + } } size = perf_evlist__mmap_size(pages); @@ -738,6 +757,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, evlist->overwrite = overwrite; evlist->mmap_len = perf_evlist__mmap_size(pages); + pr_debug("mmap size %luB\n", evlist->mmap_len); mask = evlist->mmap_len - page_size - 1; list_for_each_entry(evsel, &evlist->entries, node) { diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index ccfdeb62f576..ab71d6216803 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -361,3 +361,28 @@ int parse_nsec_time(const char *str, u64 *ptime) *ptime = time_sec * NSEC_PER_SEC + time_nsec; return 0; } + +unsigned long parse_tag_value(const char *str, struct parse_tag *tags) +{ + struct parse_tag *i = tags; + + while (i->tag) { + char *s; + + s = strchr(str, i->tag); + if (s) { + unsigned long int value; + char *endptr; + + value = strtoul(str, &endptr, 10); + if (s != endptr) + break; + + value *= i->mult; + return value; + } + i++; + } + + return (unsigned long) -1; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index a53535949043..c29ecaabf461 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -270,6 +270,13 @@ bool is_power_of_2(unsigned long n) return (n != 0 && ((n & (n - 1)) == 0)); } +static inline unsigned next_pow2(unsigned x) +{ + if (!x) + return 1; + return 1ULL << (32 - __builtin_clz(x - 1)); +} + size_t hex_width(u64 v); int hex2u64(const char *ptr, u64 *val); @@ -281,4 +288,11 @@ void dump_stack(void); extern unsigned int page_size; void get_term_dimensions(struct winsize *ws); + +struct parse_tag { + char tag; + int mult; +}; + +unsigned long parse_tag_value(const char *str, struct parse_tag *tags); #endif /* GIT_COMPAT_UTIL_H */ -- cgit v1.2.2 From b22d54b09a5448d3706929c6f0eae36429f4ec5d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 1 Sep 2013 12:36:14 +0200 Subject: perf evlist: Introduce perf_evlist__new_default function Adding new common function to create evlist with default event. It spares some code lines in automated tests. Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378031796-17892-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 12 ++++++++++++ tools/perf/util/evlist.h | 1 + 2 files changed, 13 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d21ab0812926..f0d71a9d49f4 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -50,6 +50,18 @@ struct perf_evlist *perf_evlist__new(void) return evlist; } +struct perf_evlist *perf_evlist__new_default(void) +{ + struct perf_evlist *evlist = perf_evlist__new(); + + if (evlist && perf_evlist__add_default(evlist)) { + perf_evlist__delete(evlist); + evlist = NULL; + } + + return evlist; +} + /** * perf_evlist__set_id_pos - set the positions of event ids. * @evlist: selected event list diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 4edb5008fd36..871b55ab5dee 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -53,6 +53,7 @@ struct perf_evsel_str_handler { }; struct perf_evlist *perf_evlist__new(void); +struct perf_evlist *perf_evlist__new_default(void); void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, struct thread_map *threads); void perf_evlist__exit(struct perf_evlist *evlist); -- cgit v1.2.2 From dd96c46b5c765a779d8c35cc7d1df7515b4c7baf Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 1 Sep 2013 12:36:15 +0200 Subject: perf tools: Adding throttle event data struct support Moving 'struct throttle_event' out of python code and making it global as any other event. There's no usage of throttling events in any perf commands so far (besides python support), but we'll need this event data backup for upcoming test. Signed-off-by: Jiri Olsa Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378031796-17892-5-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 7 +++++++ tools/perf/util/python.c | 7 ------- tools/perf/util/session.c | 13 +++++++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 17d9e167a7b9..9b7d4d333111 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -61,6 +61,12 @@ struct read_event { u64 id; }; +struct throttle_event { + struct perf_event_header header; + u64 time; + u64 id; + u64 stream_id; +}; #define PERF_SAMPLE_MASK \ (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ @@ -178,6 +184,7 @@ union perf_event { struct fork_event fork; struct lost_event lost; struct read_event read; + struct throttle_event throttle; struct sample_event sample; struct attr_event attr; struct event_type_event event_type; diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index a24ce0a6a941..06efd027b1db 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -33,13 +33,6 @@ int eprintf(int level, const char *fmt, ...) # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, #endif -struct throttle_event { - struct perf_event_header header; - u64 time; - u64 id; - u64 stream_id; -}; - PyMODINIT_FUNC initperf(void); #define member_def(type, member, ptype, help) \ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b97f468af955..d1e449534b33 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -397,6 +397,17 @@ static void perf_event__read_swap(union perf_event *event, bool sample_id_all) swap_sample_id_all(event, &event->read + 1); } +static void perf_event__throttle_swap(union perf_event *event, + bool sample_id_all) +{ + event->throttle.time = bswap_64(event->throttle.time); + event->throttle.id = bswap_64(event->throttle.id); + event->throttle.stream_id = bswap_64(event->throttle.stream_id); + + if (sample_id_all) + swap_sample_id_all(event, &event->throttle + 1); +} + static u8 revbyte(u8 b) { int rev = (b >> 4) | ((b & 0xf) << 4); @@ -482,6 +493,8 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_EXIT] = perf_event__task_swap, [PERF_RECORD_LOST] = perf_event__all64_swap, [PERF_RECORD_READ] = perf_event__read_swap, + [PERF_RECORD_THROTTLE] = perf_event__throttle_swap, + [PERF_RECORD_UNTHROTTLE] = perf_event__throttle_swap, [PERF_RECORD_SAMPLE] = perf_event__all64_swap, [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, -- cgit v1.2.2 From fc2be6968e99b5314f20e938a547d44dcb1c40eb Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sat, 14 Sep 2013 10:32:59 +0200 Subject: perf symbols: Add new option --ignore-vmlinux for perf top Running "perf top" on a machine with possibly invalid or non-matching vmlinux at the various places results in no symbol resolving despite /proc/kallsyms being present and valid. Add a new option --ignore-vmlinux to explicitly indicate that we do not want to use these kernels and just use what we have (kallsyms). Signed-off-by: Willy Tarreau Cc: Ingo Molnar Link: http://lkml.kernel.org/r/20130914083259.GA3418@1wt.eu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 4 ++-- tools/perf/util/symbol.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index cd1dcc45049b..48c38791d61b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1215,7 +1215,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, goto do_kallsyms; } - if (symbol_conf.vmlinux_name != NULL) { + if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { err = dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name, filter); if (err > 0) { @@ -1227,7 +1227,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, return err; } - if (vmlinux_path != NULL) { + if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) { err = dso__load_vmlinux_path(dso, map, filter); if (err > 0) return err; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 2a97bb1a8097..9b8b213a62c1 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -85,6 +85,7 @@ struct symbol_conf { unsigned short priv_size; unsigned short nr_events; bool try_vmlinux_path, + ignore_vmlinux, show_kernel_path, use_modules, sort_by_name, -- cgit v1.2.2 From fc67297b16da335d610af2fac96233d51146300a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 13 Sep 2013 15:27:43 +0900 Subject: perf tools: Separate out GTK codes to libperf-gtk.so Separate out GTK codes to a shared object called libperf-gtk.so. This time only GTK codes are built with -fPIC and libperf remains as is. Now run GTK hist and annotation browser using libdl. Signed-off-by: Namhyung Kim Reviewed-by: Pekka Enberg Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1379053663-13706-1-git-send-email-namhyung@kernel.org [ Fix it up wrt Ingo's tools/perf build speedups ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.h | 24 ------------------------ tools/perf/util/hist.h | 15 --------------- tools/perf/util/util.h | 2 ++ 3 files changed, 2 insertions(+), 39 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index f0699e9bcc6f..834b7b57b788 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -165,30 +165,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, } #endif -#ifdef HAVE_GTK2_SUPPORT -int symbol__gtk_annotate(struct symbol *sym, struct map *map, - struct perf_evsel *evsel, - struct hist_browser_timer *hbt); - -static inline int hist_entry__gtk_annotate(struct hist_entry *he, - struct perf_evsel *evsel, - struct hist_browser_timer *hbt) -{ - return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); -} - -void perf_gtk__show_annotations(void); -#else -static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, - struct perf_evsel *evsel __maybe_unused, - struct hist_browser_timer *hbt __maybe_unused) -{ - return 0; -} - -static inline void perf_gtk__show_annotations(void) {} -#endif - extern const char *disassembler_style; #endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ed4f90ebe0d5..20b175808cd3 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -228,20 +228,5 @@ static inline int script_browse(const char *script_opt __maybe_unused) #define K_SWITCH_INPUT_DATA -3000 #endif -#ifdef HAVE_GTK2_SUPPORT -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, - struct hist_browser_timer *hbt __maybe_unused, - float min_pcnt); -#else -static inline -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, - const char *help __maybe_unused, - struct hist_browser_timer *hbt __maybe_unused, - float min_pcnt __maybe_unused) -{ - return 0; -} -#endif - unsigned int hists__sort_list_width(struct hists *self); #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c29ecaabf461..7fd840bf6b62 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -128,6 +128,8 @@ void put_tracing_file(char *file); #endif #endif +#define PERF_GTK_DSO "libperf-gtk.so" + /* General helper functions */ extern void usage(const char *err) NORETURN; extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); -- cgit v1.2.2 From 963ba5fd5d04f36d6a5c9a94562484a4f270c1de Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:25 +0900 Subject: perf sort: Fix a memory leak on srcline In the hist_entry__srcline_snprintf(), path and self->srcline are pointing the same memory region, but they are doubly allocated. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b4ecc0e4c908..97cf3ef4417c 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -269,10 +269,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (!fp) goto out_ip; - if (getline(&path, &line_len, fp) < 0 || !line_len) - goto out_ip; - self->srcline = strdup(path); - if (self->srcline == NULL) + if (getline(&self->srcline, &line_len, fp) < 0 || !line_len) goto out_ip; nl = strchr(self->srcline, '\n'); -- cgit v1.2.2 From 89da393c171926d3372f573d752be5ced98038eb Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:26 +0900 Subject: perf annotate: Reuse path from the result of addr2line In the symbol__get_source_line(), path and src_line->path will have same value, but they were allocated separately, and leaks one. Just share path to src_line->path. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 7eae5488ecea..c6fd1870f278 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1143,11 +1143,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, if (getline(&path, &line_len, fp) < 0 || !line_len) goto next_close; - src_line->path = malloc(sizeof(char) * line_len + 1); - if (!src_line->path) - goto next_close; - - strcpy(src_line->path, path); + src_line->path = path; insert_source_line(&tmp_root, src_line); next_close: -- cgit v1.2.2 From 909b143162de7af310d2a9351220030260ebe728 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:27 +0900 Subject: perf hists: Free srcline when freeing hist_entry We've been leaked srcline of hist_entry, it should be freed also. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f3278a388e9a..e6fc38a86e2c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -535,6 +535,7 @@ void hist_entry__free(struct hist_entry *he) { free(he->branch_info); free(he->mem_info); + free(he->srcline); free(he); } -- cgit v1.2.2 From f048d548f803b57ee1dbf66702f398ba69657450 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:28 +0900 Subject: perf annotate: Factor out get/free_srcline() Currently external addr2line tool is used for srcline sort key and annotate with srcline info. Separate the common code to prepare upcoming enhancements. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 20 ++--------- tools/perf/util/hist.c | 2 +- tools/perf/util/sort.c | 17 ++-------- tools/perf/util/srcline.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/util.h | 5 +++ 5 files changed, 95 insertions(+), 32 deletions(-) create mode 100644 tools/perf/util/srcline.c (limited to 'tools/perf/util') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index c6fd1870f278..d48297d77e19 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1070,7 +1070,7 @@ static void symbol__free_source_line(struct symbol *sym, int len) (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); for (i = 0; i < len; i++) { - free(src_line->path); + free_srcline(src_line->path); src_line = (void *)src_line + sizeof_src_line; } @@ -1087,7 +1087,6 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, u64 start; int i, k; int evidx = evsel->idx; - char cmd[PATH_MAX * 2]; struct source_line *src_line; struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); @@ -1115,10 +1114,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, start = map__rip_2objdump(map, sym->start); for (i = 0; i < len; i++) { - char *path = NULL; - size_t line_len; u64 offset; - FILE *fp; double percent_max = 0.0; src_line->nr_pcnt = nr_pcnt; @@ -1135,19 +1131,9 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, goto next; offset = start + i; - sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); - fp = popen(cmd, "r"); - if (!fp) - goto next; - - if (getline(&path, &line_len, fp) < 0 || !line_len) - goto next_close; - - src_line->path = path; + src_line->path = get_srcline(filename, offset); insert_source_line(&tmp_root, src_line); - next_close: - pclose(fp); next: src_line = (void *)src_line + sizeof_src_line; } @@ -1188,7 +1174,7 @@ static void print_summary(struct rb_root *root, const char *filename) path = src_line->path; color = get_percent_color(percent_max); - color_fprintf(stdout, color, " %s", path); + color_fprintf(stdout, color, " %s\n", path); node = rb_next(node); } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e6fc38a86e2c..cca03831f41a 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -535,7 +535,7 @@ void hist_entry__free(struct hist_entry *he) { free(he->branch_info); free(he->mem_info); - free(he->srcline); + free_srcline(he->srcline); free(he); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 97cf3ef4417c..b7e0ef0445de 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -251,8 +251,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, unsigned int width __maybe_unused) { FILE *fp = NULL; - char cmd[PATH_MAX + 2], *path = self->srcline, *nl; - size_t line_len; + char *path = self->srcline; if (path != NULL) goto out_path; @@ -263,19 +262,9 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) goto out_ip; - snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, - self->ms.map->dso->long_name, self->ip); - fp = popen(cmd, "r"); - if (!fp) - goto out_ip; - - if (getline(&self->srcline, &line_len, fp) < 0 || !line_len) - goto out_ip; + path = get_srcline(self->ms.map->dso->long_name, self->ip); + self->srcline = path; - nl = strchr(self->srcline, '\n'); - if (nl != NULL) - *nl = '\0'; - path = self->srcline; out_path: if (fp) pclose(fp); diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c new file mode 100644 index 000000000000..7e92cca6f502 --- /dev/null +++ b/tools/perf/util/srcline.c @@ -0,0 +1,83 @@ +#include +#include +#include + +#include + +#include "util/util.h" +#include "util/debug.h" + +static int addr2line(const char *dso_name, unsigned long addr, + char **file, unsigned int *line_nr) +{ + FILE *fp; + char cmd[PATH_MAX]; + char *filename = NULL; + size_t len; + char *sep; + int ret = 0; + + scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, + dso_name, addr); + + fp = popen(cmd, "r"); + if (fp == NULL) { + pr_warning("popen failed for %s\n", dso_name); + return 0; + } + + if (getline(&filename, &len, fp) < 0 || !len) { + pr_warning("addr2line has no output for %s\n", dso_name); + goto out; + } + + sep = strchr(filename, '\n'); + if (sep) + *sep = '\0'; + + if (!strcmp(filename, "??:0")) { + pr_debug("no debugging info in %s\n", dso_name); + free(filename); + goto out; + } + + sep = strchr(filename, ':'); + if (sep) { + *sep++ = '\0'; + *file = filename; + *line_nr = strtoul(sep, NULL, 0); + ret = 1; + } +out: + pclose(fp); + return ret; +} + +char *get_srcline(const char *dso_name, unsigned long addr) +{ + char *file; + unsigned line; + char *srcline; + size_t size; + + if (!addr2line(dso_name, addr, &file, &line)) + return SRCLINE_UNKNOWN; + + /* just calculate actual length */ + size = snprintf(NULL, 0, "%s:%u", file, line) + 1; + + srcline = malloc(size); + if (srcline) + snprintf(srcline, size, "%s:%u", file, line); + else + srcline = SRCLINE_UNKNOWN; + + free(file); + return srcline; +} + +void free_srcline(char *srcline) +{ + if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) + free(srcline); +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 7fd840bf6b62..7c8b43fa8898 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -297,4 +297,9 @@ struct parse_tag { }; unsigned long parse_tag_value(const char *str, struct parse_tag *tags); + +#define SRCLINE_UNKNOWN ((char *) "??:0") + +char *get_srcline(const char *dso_name, unsigned long addr); +void free_srcline(char *srcline); #endif /* GIT_COMPAT_UTIL_H */ -- cgit v1.2.2 From 58d91a0068694a5ba3efc99e88ce6b4b0dd0d085 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:29 +0900 Subject: perf tools: Do not try to call addr2line on non-binary files No need to call addr2line since they don't have such information. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 3 --- tools/perf/util/srcline.c | 11 +++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b7e0ef0445de..d4435939c774 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -259,9 +259,6 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (!self->ms.map) goto out_ip; - if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) - goto out_ip; - path = get_srcline(self->ms.map->dso->long_name, self->ip); self->srcline = path; diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 7e92cca6f502..777f91880cdb 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -57,11 +57,17 @@ char *get_srcline(const char *dso_name, unsigned long addr) { char *file; unsigned line; - char *srcline; + char *srcline = SRCLINE_UNKNOWN; size_t size; + if (dso_name[0] == '[') + goto out; + + if (!strncmp(dso_name, "/tmp/perf-", 10)) + goto out; + if (!addr2line(dso_name, addr, &file, &line)) - return SRCLINE_UNKNOWN; + goto out; /* just calculate actual length */ size = snprintf(NULL, 0, "%s:%u", file, line) + 1; @@ -73,6 +79,7 @@ char *get_srcline(const char *dso_name, unsigned long addr) srcline = SRCLINE_UNKNOWN; free(file); +out: return srcline; } -- cgit v1.2.2 From 86c98cab5a3137376ea7df5ffa5bd52e545fee95 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:30 +0900 Subject: perf annotate: Pass dso instead of dso_name to get_srcline() This is a preparation of next change. No functional changes are intended. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 11 ++++------- tools/perf/util/dso.h | 1 + tools/perf/util/sort.c | 2 +- tools/perf/util/srcline.c | 4 +++- tools/perf/util/util.h | 4 +++- 5 files changed, 12 insertions(+), 10 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d48297d77e19..d73e8008aada 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1081,8 +1081,7 @@ static void symbol__free_source_line(struct symbol *sym, int len) /* Get the filename:line for the colored entries */ static int symbol__get_source_line(struct symbol *sym, struct map *map, struct perf_evsel *evsel, - struct rb_root *root, int len, - const char *filename) + struct rb_root *root, int len) { u64 start; int i, k; @@ -1131,7 +1130,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, goto next; offset = start + i; - src_line->path = get_srcline(filename, offset); + src_line->path = get_srcline(map->dso, offset); insert_source_line(&tmp_root, src_line); next: @@ -1338,7 +1337,6 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, bool full_paths, int min_pcnt, int max_lines) { struct dso *dso = map->dso; - const char *filename = dso->long_name; struct rb_root source_line = RB_ROOT; u64 len; @@ -1348,9 +1346,8 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, len = symbol__size(sym); if (print_lines) { - symbol__get_source_line(sym, map, evsel, &source_line, - len, filename); - print_summary(&source_line, filename); + symbol__get_source_line(sym, map, evsel, &source_line, len); + print_summary(&source_line, dso->long_name); } symbol__annotate_printf(sym, map, evsel, full_paths, diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index dbd9241ea290..72eedd65fc2d 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -6,6 +6,7 @@ #include #include "types.h" #include "map.h" +#include "build-id.h" enum dso_binary_type { DSO_BINARY_TYPE__KALLSYMS = 0, diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d4435939c774..f732120e8bc6 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -259,7 +259,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (!self->ms.map) goto out_ip; - path = get_srcline(self->ms.map->dso->long_name, self->ip); + path = get_srcline(self->ms.map->dso, self->ip); self->srcline = path; out_path: diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 777f91880cdb..c736d9428cf2 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -4,6 +4,7 @@ #include +#include "util/dso.h" #include "util/util.h" #include "util/debug.h" @@ -53,11 +54,12 @@ out: return ret; } -char *get_srcline(const char *dso_name, unsigned long addr) +char *get_srcline(struct dso *dso, unsigned long addr) { char *file; unsigned line; char *srcline = SRCLINE_UNKNOWN; + char *dso_name = dso->long_name; size_t size; if (dso_name[0] == '[') diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 7c8b43fa8898..1f06ba44cd53 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -300,6 +300,8 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags); #define SRCLINE_UNKNOWN ((char *) "??:0") -char *get_srcline(const char *dso_name, unsigned long addr); +struct dso; + +char *get_srcline(struct dso *dso, unsigned long addr); void free_srcline(char *srcline); #endif /* GIT_COMPAT_UTIL_H */ -- cgit v1.2.2 From 2cc9d0ef577975abb3ebce7d5978559ec1c73633 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:31 +0900 Subject: perf tools: Save failed result of get_srcline() Some dso's lack srcline info, so there's no point to keep trying on them. Just save failture status and skip them. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 1 + tools/perf/util/dso.h | 1 + tools/perf/util/srcline.c | 10 ++++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 6bfc8aacaf7c..af4c687cc49b 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -450,6 +450,7 @@ struct dso *dso__new(const char *name) dso->rel = 0; dso->sorted_by_name = 0; dso->has_build_id = 0; + dso->has_srcline = 1; dso->kernel = DSO_TYPE_USER; dso->needs_swap = DSO_SWAP__UNSET; INIT_LIST_HEAD(&dso->node); diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 72eedd65fc2d..9ac666abbe7e 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -83,6 +83,7 @@ struct dso { enum dso_binary_type data_type; u8 adjust_symbols:1; u8 has_build_id:1; + u8 has_srcline:1; u8 hit:1; u8 annotate_warned:1; u8 sname_alloc:1; diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index c736d9428cf2..dcff10bed7da 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -58,10 +58,13 @@ char *get_srcline(struct dso *dso, unsigned long addr) { char *file; unsigned line; - char *srcline = SRCLINE_UNKNOWN; + char *srcline; char *dso_name = dso->long_name; size_t size; + if (!dso->has_srcline) + return SRCLINE_UNKNOWN; + if (dso_name[0] == '[') goto out; @@ -81,8 +84,11 @@ char *get_srcline(struct dso *dso, unsigned long addr) srcline = SRCLINE_UNKNOWN; free(file); -out: return srcline; + +out: + dso->has_srcline = 0; + return SRCLINE_UNKNOWN; } void free_srcline(char *srcline) -- cgit v1.2.2 From 2f48fcd84e9e68392e29c59204a4a434311d49e9 Mon Sep 17 00:00:00 2001 From: Roberto Vitillo Date: Wed, 11 Sep 2013 14:09:32 +0900 Subject: perf tools: Implement addr2line directly using libbfd When the srcline sort key is used , the external addr2line utility needs to be run for each hist entry to get the srcline info. This can consume quite a time if one has a huge perf.data file. So rather than executing the external utility, implement it internally and just call it. We can do it since we've linked with libbfd already. Signed-off-by: Roberto Agostino Vitillo Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-9-git-send-email-namhyung@kernel.org [ Use a2l_data struct instead of static globals ] Signed-off-by: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/srcline.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index dcff10bed7da..37353194e0f6 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -8,6 +8,172 @@ #include "util/util.h" #include "util/debug.h" +#ifdef HAVE_LIBBFD_SUPPORT + +/* + * Implement addr2line using libbfd. + */ +#define PACKAGE "perf" +#include + +struct a2l_data { + const char *input; + unsigned long addr; + + bool found; + const char *filename; + const char *funcname; + unsigned line; + + bfd *abfd; + asymbol **syms; +}; + +static int bfd_error(const char *string) +{ + const char *errmsg; + + errmsg = bfd_errmsg(bfd_get_error()); + fflush(stdout); + + if (string) + pr_debug("%s: %s\n", string, errmsg); + else + pr_debug("%s\n", errmsg); + + return -1; +} + +static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) +{ + long storage; + long symcount; + asymbol **syms; + bfd_boolean dynamic = FALSE; + + if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) + return bfd_error(bfd_get_filename(abfd)); + + storage = bfd_get_symtab_upper_bound(abfd); + if (storage == 0L) { + storage = bfd_get_dynamic_symtab_upper_bound(abfd); + dynamic = TRUE; + } + if (storage < 0L) + return bfd_error(bfd_get_filename(abfd)); + + syms = malloc(storage); + if (dynamic) + symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); + else + symcount = bfd_canonicalize_symtab(abfd, syms); + + if (symcount < 0) { + free(syms); + return bfd_error(bfd_get_filename(abfd)); + } + + a2l->syms = syms; + return 0; +} + +static void find_address_in_section(bfd *abfd, asection *section, void *data) +{ + bfd_vma pc, vma; + bfd_size_type size; + struct a2l_data *a2l = data; + + if (a2l->found) + return; + + if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) + return; + + pc = a2l->addr; + vma = bfd_get_section_vma(abfd, section); + size = bfd_get_section_size(section); + + if (pc < vma || pc >= vma + size) + return; + + a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, + &a2l->filename, &a2l->funcname, + &a2l->line); +} + +static struct a2l_data *addr2line_init(const char *path) +{ + bfd *abfd; + struct a2l_data *a2l = NULL; + + abfd = bfd_openr(path, NULL); + if (abfd == NULL) + return NULL; + + if (!bfd_check_format(abfd, bfd_object)) + goto out; + + a2l = zalloc(sizeof(*a2l)); + if (a2l == NULL) + goto out; + + a2l->abfd = abfd; + a2l->input = strdup(path); + if (a2l->input == NULL) + goto out; + + if (slurp_symtab(abfd, a2l)) + goto out; + + return a2l; + +out: + if (a2l) { + free((void *)a2l->input); + free(a2l); + } + bfd_close(abfd); + return NULL; +} + +static void addr2line_cleanup(struct a2l_data *a2l) +{ + if (a2l->abfd) + bfd_close(a2l->abfd); + free((void *)a2l->input); + free(a2l->syms); + free(a2l); +} + +static int addr2line(const char *dso_name, unsigned long addr, + char **file, unsigned int *line) +{ + int ret = 0; + struct a2l_data *a2l; + + a2l = addr2line_init(dso_name); + if (a2l == NULL) { + pr_warning("addr2line_init failed for %s\n", dso_name); + return 0; + } + + a2l->addr = addr; + bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); + + if (a2l->found && a2l->filename) { + *file = strdup(a2l->filename); + *line = a2l->line; + + if (*file) + ret = 1; + } + + addr2line_cleanup(a2l); + return ret; +} + +#else /* HAVE_LIBBFD_SUPPORT */ + static int addr2line(const char *dso_name, unsigned long addr, char **file, unsigned int *line_nr) { @@ -53,6 +219,7 @@ out: pclose(fp); return ret; } +#endif /* HAVE_LIBBFD_SUPPORT */ char *get_srcline(struct dso *dso, unsigned long addr) { -- cgit v1.2.2 From 4adcc43003024354b45edadfad4ea2fa205f135f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 11 Sep 2013 14:09:33 +0900 Subject: perf tools: Fix srcline sort key behavior Currently the srcline sort key compares ip rather than srcline info. I guess this was due to a performance reason to run external addr2line utility. Now we have implemented the functionality inside, use the srcline info when comparing hist entries. Also constantly print "??:0" string for unknown srcline rather than printing ip. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1378876173-13363-10-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index f732120e8bc6..32c56377e008 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -243,33 +243,32 @@ struct sort_entry sort_sym = { static int64_t sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) { - return (int64_t)(right->ip - left->ip); + if (!left->srcline) { + if (!left->ms.map) + left->srcline = SRCLINE_UNKNOWN; + else { + struct map *map = left->ms.map; + left->srcline = get_srcline(map->dso, + map__rip_2objdump(map, left->ip)); + } + } + if (!right->srcline) { + if (!right->ms.map) + right->srcline = SRCLINE_UNKNOWN; + else { + struct map *map = right->ms.map; + right->srcline = get_srcline(map->dso, + map__rip_2objdump(map, right->ip)); + } + } + return strcmp(left->srcline, right->srcline); } static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width __maybe_unused) { - FILE *fp = NULL; - char *path = self->srcline; - - if (path != NULL) - goto out_path; - - if (!self->ms.map) - goto out_ip; - - path = get_srcline(self->ms.map->dso, self->ip); - self->srcline = path; - -out_path: - if (fp) - pclose(fp); - return repsep_snprintf(bf, size, "%s", path); -out_ip: - if (fp) - pclose(fp); - return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); + return repsep_snprintf(bf, size, "%s", self->srcline); } struct sort_entry sort_srcline = { -- cgit v1.2.2 From f4be904d2fa7c65e230ed4b1008ebdf5f4053ac3 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Sun, 22 Sep 2013 13:22:09 +0300 Subject: perf machine: Use snprintf instead of sprintf To avoid buffer overruns. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1379845338-29637-2-git-send-email-adrian.hunter@intel.com [ Split from aa7fe3b ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6188d2876a71..ddf917b787fa 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -785,10 +785,10 @@ static int machine__create_modules(struct machine *machine) const char *modules; char path[PATH_MAX]; - if (machine__is_default_guest(machine)) + if (machine__is_default_guest(machine)) { modules = symbol_conf.default_guest_modules; - else { - sprintf(path, "%s/proc/modules", machine->root_dir); + } else { + snprintf(path, PATH_MAX, "%s/proc/modules", machine->root_dir); modules = path; } -- cgit v1.2.2 From 820042233bd3c1b24b5ca4c75a88a4e0de39814a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 27 Sep 2013 18:29:58 +0200 Subject: perf tools: Move start conditions to start of the flex file Moving start conditions to start of the flex file so it's clear what the INITIAL condition rules are. Plus adding default rule for INITIAL condition. This prevents default space to be printed for events like: $ ./perf stat -e "cycles " kill 2>/dev/null $ ^^^^^^^^ Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1380299398-10839-1-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.l | 63 +++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 91346b753960..343299575b30 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -126,6 +126,37 @@ modifier_bp [rwx]{1,3} } +{ +config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } +config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } +config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } +name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } +period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } +branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } +, { return ','; } +"/" { BEGIN(INITIAL); return '/'; } +{name_minus} { return str(yyscanner, PE_NAME); } +} + +{ +{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } +: { return ':'; } +{num_dec} { return value(yyscanner, 10); } +{num_hex} { return value(yyscanner, 16); } + /* + * We need to separate 'mem:' scanner part, in order to get specific + * modifier bits parsed out. Otherwise we would need to handle PE_NAME + * and we'd need to parse it manually. During the escape from + * state we need to put the escaping char back, so we dont miss it. + */ +. { unput(*yytext); BEGIN(INITIAL); } + /* + * We destroy the scanner after reaching EOF, + * but anyway just to be sure get back to INIT state. + */ +<> { BEGIN(INITIAL); } +} + cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } @@ -162,18 +193,6 @@ speculative-read|speculative-load | refs|Reference|ops|access | misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } -{ -config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } -config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } -config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } -name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } -period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } -branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } -, { return ','; } -"/" { BEGIN(INITIAL); return '/'; } -{name_minus} { return str(yyscanner, PE_NAME); } -} - mem: { BEGIN(mem); return PE_PREFIX_MEM; } r{num_raw_hex} { return raw(yyscanner); } {num_dec} { return value(yyscanner, 10); } @@ -189,25 +208,7 @@ r{num_raw_hex} { return raw(yyscanner); } "}" { return '}'; } = { return '='; } \n { } - -{ -{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } -: { return ':'; } -{num_dec} { return value(yyscanner, 10); } -{num_hex} { return value(yyscanner, 16); } - /* - * We need to separate 'mem:' scanner part, in order to get specific - * modifier bits parsed out. Otherwise we would need to handle PE_NAME - * and we'd need to parse it manually. During the escape from - * state we need to put the escaping char back, so we dont miss it. - */ -. { unput(*yytext); BEGIN(INITIAL); } - /* - * We destroy the scanner after reaching EOF, - * but anyway just to be sure get back to INIT state. - */ -<> { BEGIN(INITIAL); } -} +. { } %% -- cgit v1.2.2 From a65cb4b9f8a777a715371c63c0525408048cea3e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 2 Oct 2013 15:46:39 +0200 Subject: perf evlist: Fix perf_evlist__mmap_read event overflow The perf_evlist__mmap_read used 'union perf_event' as a placeholder for event crossing the mmap boundary. This is ok for sample shorter than ~PATH_MAX. However we could grow up to the maximum sample size which is 16 bits max. I hit this overflow issue when using 'perf top -G dwarf' which produces sample with the size around 8192 bytes. We could configure any valid sample size here using: '-G dwarf,size'. Using array with sample max size instead for the event placeholder. Also adding another safe check for the dynamic size of the user stack. TODO: The 'struct perf_mmap' is quite big now, maybe we could use some lazy allocation for event_copy size. Signed-off-by: Jiri Olsa Acked-by: David Ahern Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1380721599-24285-1-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 3 +++ tools/perf/util/evlist.c | 4 ++-- tools/perf/util/evlist.h | 2 +- tools/perf/util/evsel.c | 3 +++ 4 files changed, 9 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 9b7d4d333111..752709ccfb00 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -75,6 +75,9 @@ struct throttle_event { PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | \ PERF_SAMPLE_IDENTIFIER) +/* perf sample has 16 bits size limit */ +#define PERF_SAMPLE_MAX_SIZE (1 << 16) + struct sample_event { struct perf_event_header header; u64 array[]; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index f0d71a9d49f4..cb9523f50a37 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -540,7 +540,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) if ((old & md->mask) + size != ((old + size) & md->mask)) { unsigned int offset = old; unsigned int len = min(sizeof(*event), size), cpy; - void *dst = &md->event_copy; + void *dst = md->event_copy; do { cpy = min(md->mask + 1 - (offset & md->mask), len); @@ -550,7 +550,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) len -= cpy; } while (len); - event = &md->event_copy; + event = (union perf_event *) md->event_copy; } old += size; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 871b55ab5dee..722618f84c53 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -21,7 +21,7 @@ struct perf_mmap { void *base; int mask; unsigned int prev; - union perf_event event_copy; + char event_copy[PERF_SAMPLE_MAX_SIZE]; }; struct perf_evlist { diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index abe69af58b62..bfebc1ea3c51 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1456,6 +1456,9 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, array = (void *)array + sz; OVERFLOW_CHECK_u64(array); data->user_stack.size = *array++; + if (WARN_ONCE(data->user_stack.size > sz, + "user stack dump failure\n")) + return -EFAULT; } } -- cgit v1.2.2 From 8fb598e5a3b0ac213012e8461a309843ba0f2e74 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 13:13:00 -0600 Subject: perf trace: Fix comm resolution when reading events from file Task comm's are getting lost when processing events from a file. The problem is that the trace struct used by the live processing has its host machine and the perf-session used for file based processing has its host machine. Fix by having both references point to the same machine. Before: 0.030 ( 0.001 ms): :27743/27743 brk( ... 0.057 ( 0.004 ms): :27743/27743 mmap(len: 4096, prot: READ|WRITE, flags: ... 0.075 ( 0.006 ms): :27743/27743 access(filename: 0x7f3809fbce00, mode: R ... 0.091 ( 0.005 ms): :27743/27743 open(filename: 0x7f3809fba14c, flags: CLOEXEC ... ... After: 0.030 ( 0.001 ms): make/27743 brk( ... 0.057 ( 0.004 ms): make/27743 mmap(len: 4096, prot: READ|WRITE, flags: ... 0.075 ( 0.006 ms): make/27743 access(filename: 0x7f3809fbce00, mode: R ... 0.091 ( 0.005 ms): make/27743 open(filename: 0x7f3809fba14c, flags: CLOEXEC ... ... Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380395584-9025-4-git-send-email-dsahern@gmail.com [ Moved creation of new host machine to a separate constructor: machine__new_host() ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 17 +++++++++++++++++ tools/perf/util/machine.h | 1 + 2 files changed, 18 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ddf917b787fa..fc14f9bf82c8 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -46,6 +46,23 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) return 0; } +struct machine *machine__new_host(void) +{ + struct machine *machine = malloc(sizeof(*machine)); + + if (machine != NULL) { + machine__init(machine, "", HOST_KERNEL_ID); + + if (machine__create_kernel_maps(machine) < 0) + goto out_delete; + } + + return machine; +out_delete: + free(machine); + return NULL; +} + static void dsos__delete(struct list_head *dsos) { struct dso *pos, *n; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 58a6be1fc739..5150d5e1db67 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -74,6 +74,7 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size); void machines__set_symbol_filter(struct machines *machines, symbol_filter_t symbol_filter); +struct machine *machine__new_host(void); int machine__init(struct machine *machine, const char *root_dir, pid_t pid); void machine__exit(struct machine *machine); void machine__delete_dead_threads(struct machine *machine); -- cgit v1.2.2 From 35feee19f9fda7447f51073b5be3f6d082b508f5 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 13:12:58 -0600 Subject: perf machine: Add method to loop over threads and invoke handler Loop over all threads within a machine - including threads moved to the dead threads list -- and invoked a function. This allows commands to run some specific function on each thread (eg., dump statistics) yet hides how the threads are maintained within the machine. Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380395584-9025-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 23 +++++++++++++++++++++++ tools/perf/util/machine.h | 4 ++++ 2 files changed, 27 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index fc14f9bf82c8..901397ae82be 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1393,3 +1393,26 @@ int machine__resolve_callchain(struct machine *machine, sample); } + +int machine__for_each_thread(struct machine *machine, + int (*fn)(struct thread *thread, void *p), + void *priv) +{ + struct rb_node *nd; + struct thread *thread; + int rc = 0; + + for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { + thread = rb_entry(nd, struct thread, rb_node); + rc = fn(thread, priv); + if (rc != 0) + return rc; + } + + list_for_each_entry(thread, &machine->dead_threads, node) { + rc = fn(thread, priv); + if (rc != 0) + return rc; + } + return rc; +} diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 5150d5e1db67..d44c09bdc45e 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -166,4 +166,8 @@ void machines__destroy_kernel_maps(struct machines *machines); size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); +int machine__for_each_thread(struct machine *machine, + int (*fn)(struct thread *thread, void *p), + void *priv); + #endif /* __PERF_MACHINE_H */ -- cgit v1.2.2 From 2969b12993ca7a8b9692048431e075a67815002d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 28 Sep 2013 13:13:02 -0600 Subject: perf intlist: Add priv member Allows commands to leverage intlist infrastructure for opaque structures. For example an upcoming perf-trace change will use this as a means of tracking syscalls statistics by task. Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1380395584-9025-6-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intlist.c | 1 + tools/perf/util/intlist.h | 1 + 2 files changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 11a8d86f7fea..826d7b38c9a0 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c @@ -20,6 +20,7 @@ static struct rb_node *intlist__node_new(struct rblist *rblist __maybe_unused, if (node != NULL) { node->i = i; + node->priv = NULL; rc = &node->rb_node; } diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 62351dad848f..0eb00ac39e01 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h @@ -9,6 +9,7 @@ struct int_node { struct rb_node rb_node; int i; + void *priv; }; struct intlist { -- cgit v1.2.2 From 316d70d6dbde540b275289563cbddd9f0c903fc6 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 8 Oct 2013 11:45:48 +0300 Subject: perf symbols: Make a separate function to parse /proc/modules Make a separate function to parse /proc/modules so that it can be reused. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381221956-16699-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 67 +++++++++++++---------------------------------- tools/perf/util/symbol.c | 58 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/symbol.h | 3 +++ 3 files changed, 79 insertions(+), 49 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 901397ae82be..6b861aefd99a 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -793,12 +793,22 @@ static int machine__set_modules_path(struct machine *machine) return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); } -static int machine__create_modules(struct machine *machine) +static int machine__create_module(void *arg, const char *name, u64 start) { - char *line = NULL; - size_t n; - FILE *file; + struct machine *machine = arg; struct map *map; + + map = machine__new_module(machine, start, name); + if (map == NULL) + return -1; + + dso__kernel_module_get_build_id(map->dso, machine->root_dir); + + return 0; +} + +static int machine__create_modules(struct machine *machine) +{ const char *modules; char path[PATH_MAX]; @@ -812,56 +822,15 @@ static int machine__create_modules(struct machine *machine) if (symbol__restricted_filename(modules, "/proc/modules")) return -1; - file = fopen(modules, "r"); - if (file == NULL) + if (modules__parse(modules, machine, machine__create_module)) return -1; - while (!feof(file)) { - char name[PATH_MAX]; - u64 start; - char *sep; - int line_len; - - line_len = getline(&line, &n, file); - if (line_len < 0) - break; - - if (!line) - goto out_failure; - - line[--line_len] = '\0'; /* \n */ - - sep = strrchr(line, 'x'); - if (sep == NULL) - continue; - - hex2u64(sep + 1, &start); - - sep = strchr(line, ' '); - if (sep == NULL) - continue; - - *sep = '\0'; - - snprintf(name, sizeof(name), "[%s]", line); - map = machine__new_module(machine, start, name); - if (map == NULL) - goto out_delete_line; - dso__kernel_module_get_build_id(map->dso, machine->root_dir); - } + if (!machine__set_modules_path(machine)) + return 0; - free(line); - fclose(file); + pr_debug("Problems setting modules path maps, continuing anyway...\n"); - if (machine__set_modules_path(machine) < 0) { - pr_debug("Problems setting modules path maps, continuing anyway...\n"); - } return 0; - -out_delete_line: - free(line); -out_failure: - return -1; } int machine__create_kernel_maps(struct machine *machine) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 48c38791d61b..5fd95135e838 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -500,6 +500,64 @@ out_failure: return -1; } +int modules__parse(const char *filename, void *arg, + int (*process_module)(void *arg, const char *name, + u64 start)) +{ + char *line = NULL; + size_t n; + FILE *file; + int err = 0; + + file = fopen(filename, "r"); + if (file == NULL) + return -1; + + while (1) { + char name[PATH_MAX]; + u64 start; + char *sep; + ssize_t line_len; + + line_len = getline(&line, &n, file); + if (line_len < 0) { + if (feof(file)) + break; + err = -1; + goto out; + } + + if (!line) { + err = -1; + goto out; + } + + line[--line_len] = '\0'; /* \n */ + + sep = strrchr(line, 'x'); + if (sep == NULL) + continue; + + hex2u64(sep + 1, &start); + + sep = strchr(line, ' '); + if (sep == NULL) + continue; + + *sep = '\0'; + + scnprintf(name, sizeof(name), "[%s]", line); + + err = process_module(arg, name, start); + if (err) + break; + } +out: + free(line); + fclose(file); + return err; +} + struct process_kallsyms_args { struct map *map; struct dso *dso; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 9b8b213a62c1..2d3eb43ab9f9 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -223,6 +223,9 @@ int sysfs__read_build_id(const char *filename, void *bf, size_t size); int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); +int modules__parse(const char *filename, void *arg, + int (*process_module)(void *arg, const char *name, + u64 start)); int filename__read_debuglink(const char *filename, char *debuglink, size_t size); -- cgit v1.2.2 From 3fae82db558468c01f36eb6398e9459ac240e697 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 9 Oct 2013 11:49:28 +0200 Subject: perf tools: Align perf version output to other build messages Before: CC util/pmu.o CC util/parse-events.o PERF_VERSION = 3.12.rc4.g1b30c CC util/parse-events-flex.o GEN perf-archive After: CC util/pmu.o CC util/parse-events.o PERF_VERSION = 3.12.rc4.g1b30c CC util/parse-events-flex.o GEN perf-archive Signed-off-by: Ingo Molnar Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381312169-17354-4-git-send-email-mingo@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/PERF-VERSION-GEN | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 15a77b7c0e36..ce7a804b2951 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -40,7 +40,7 @@ else VC=unset fi test "$VN" = "$VC" || { - echo >&2 "PERF_VERSION = $VN" + echo >&2 " PERF_VERSION = $VN" echo "#define PERF_VERSION \"$VN\"" >$GVF } -- cgit v1.2.2 From 813335b8b27d9ceeb67a073f501ada8b8dde37a7 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 8 Oct 2013 21:26:52 -0600 Subject: perf util: Add findnew method to intlist Similar to other findnew based methods if the requested object is not found, add it to the list. v2: followed format of other findnew methods per acme's request Signed-off-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381289214-24885-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intlist.c | 22 ++++++++++++++++++---- tools/perf/util/intlist.h | 1 + tools/perf/util/rblist.c | 27 ++++++++++++++++++++++++--- tools/perf/util/rblist.h | 1 + 4 files changed, 44 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 826d7b38c9a0..89715b64a315 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c @@ -58,22 +58,36 @@ void intlist__remove(struct intlist *ilist, struct int_node *node) rblist__remove_node(&ilist->rblist, &node->rb_node); } -struct int_node *intlist__find(struct intlist *ilist, int i) +static struct int_node *__intlist__findnew(struct intlist *ilist, + int i, bool create) { - struct int_node *node; + struct int_node *node = NULL; struct rb_node *rb_node; if (ilist == NULL) return NULL; - node = NULL; - rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); + if (create) + rb_node = rblist__findnew(&ilist->rblist, (void *)((long)i)); + else + rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); + if (rb_node) node = container_of(rb_node, struct int_node, rb_node); return node; } +struct int_node *intlist__find(struct intlist *ilist, int i) +{ + return __intlist__findnew(ilist, i, false); +} + +struct int_node *intlist__findnew(struct intlist *ilist, int i) +{ + return __intlist__findnew(ilist, i, true); +} + static int intlist__parse_list(struct intlist *ilist, const char *s) { char *sep; diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 0eb00ac39e01..aa6877d36858 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h @@ -24,6 +24,7 @@ int intlist__add(struct intlist *ilist, int i); struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx); struct int_node *intlist__find(struct intlist *ilist, int i); +struct int_node *intlist__findnew(struct intlist *ilist, int i); static inline bool intlist__has_entry(struct intlist *ilist, int i) { diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c index a16cdd2625ad..0dfe27d99458 100644 --- a/tools/perf/util/rblist.c +++ b/tools/perf/util/rblist.c @@ -48,10 +48,12 @@ void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node) rblist->node_delete(rblist, rb_node); } -struct rb_node *rblist__find(struct rblist *rblist, const void *entry) +static struct rb_node *__rblist__findnew(struct rblist *rblist, + const void *entry, + bool create) { struct rb_node **p = &rblist->entries.rb_node; - struct rb_node *parent = NULL; + struct rb_node *parent = NULL, *new_node = NULL; while (*p != NULL) { int rc; @@ -67,7 +69,26 @@ struct rb_node *rblist__find(struct rblist *rblist, const void *entry) return parent; } - return NULL; + if (create) { + new_node = rblist->node_new(rblist, entry); + if (new_node) { + rb_link_node(new_node, parent, p); + rb_insert_color(new_node, &rblist->entries); + ++rblist->nr_entries; + } + } + + return new_node; +} + +struct rb_node *rblist__find(struct rblist *rblist, const void *entry) +{ + return __rblist__findnew(rblist, entry, false); +} + +struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry) +{ + return __rblist__findnew(rblist, entry, true); } void rblist__init(struct rblist *rblist) diff --git a/tools/perf/util/rblist.h b/tools/perf/util/rblist.h index 6d0cae5ae83d..ff9913b994c2 100644 --- a/tools/perf/util/rblist.h +++ b/tools/perf/util/rblist.h @@ -32,6 +32,7 @@ void rblist__delete(struct rblist *rblist); int rblist__add_node(struct rblist *rblist, const void *new_entry); void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node); struct rb_node *rblist__find(struct rblist *rblist, const void *entry); +struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry); struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx); static inline bool rblist__empty(const struct rblist *rblist) -- cgit v1.2.2 From a949fffb84df6f9be136198a00f796a9dc696bd0 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 9 Oct 2013 21:51:31 -0600 Subject: perf tools: Fix old GCC build error in 'get_srcline' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trace-event-parse.c:parse_proc_kallsyms() Old GCC (4.4.2) does not see through the code flow of get_srcline() and gets confused about the status of 'file' and 'line': CC /tmp/build/perf/util/srcline.o cc1: warnings being treated as errors util/srcline.c: In function ¿get_srcline¿: util/srcline.c:226: error: ¿file¿ may be used uninitialized in this function util/srcline.c:227: error: ¿line¿ may be used uninitialized in this function make[1]: *** [/tmp/build/perf/util/srcline.o] Error 1 make: *** [install] Error 2 make: Leaving directory `/home/acme/git/linux/tools/perf' [acme@fedora12 linux]$ Help out GCC by initializing 'file' and 'line'. Signed-off-by: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/n/tip-h8k7h49z3cndqgjdftkmm9f8@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/srcline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 37353194e0f6..d11aefbc4b8d 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -223,8 +223,8 @@ out: char *get_srcline(struct dso *dso, unsigned long addr) { - char *file; - unsigned line; + char *file = NULL; + unsigned line = 0; char *srcline; char *dso_name = dso->long_name; size_t size; -- cgit v1.2.2 From 52afdaf9f0c6a35e154ba42ac9510044e16d75ec Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 9 Oct 2013 15:01:11 +0300 Subject: perf symbols: Validate kcore module addresses Before using kcore we need to check that modules are in memory at the same addresses that they were when data was recorded. This is done because, while we could remap symbols to different addresses, the object code linkages would still be different which would provide an erroneous view of the object code. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381320078-16497-2-git-send-email-adrian.hunter@intel.com [ Rename basename to base_name to avoid shadowing libgen's basename in fedora 12 ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 196 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 175 insertions(+), 21 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 5fd95135e838..b2f60ddc864f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -798,51 +798,201 @@ bool symbol__restricted_filename(const char *filename, return restricted; } -struct kcore_mapfn_data { - struct dso *dso; - enum map_type type; - struct list_head maps; +struct module_info { + struct rb_node rb_node; + char *name; + u64 start; }; -static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) +static void add_module(struct module_info *mi, struct rb_root *modules) { - struct kcore_mapfn_data *md = data; - struct map *map; + struct rb_node **p = &modules->rb_node; + struct rb_node *parent = NULL; + struct module_info *m; - map = map__new2(start, md->dso, md->type); - if (map == NULL) + while (*p != NULL) { + parent = *p; + m = rb_entry(parent, struct module_info, rb_node); + if (strcmp(mi->name, m->name) < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&mi->rb_node, parent, p); + rb_insert_color(&mi->rb_node, modules); +} + +static void delete_modules(struct rb_root *modules) +{ + struct module_info *mi; + struct rb_node *next = rb_first(modules); + + while (next) { + mi = rb_entry(next, struct module_info, rb_node); + next = rb_next(&mi->rb_node); + rb_erase(&mi->rb_node, modules); + free(mi->name); + free(mi); + } +} + +static struct module_info *find_module(const char *name, + struct rb_root *modules) +{ + struct rb_node *n = modules->rb_node; + + while (n) { + struct module_info *m; + int cmp; + + m = rb_entry(n, struct module_info, rb_node); + cmp = strcmp(name, m->name); + if (cmp < 0) + n = n->rb_left; + else if (cmp > 0) + n = n->rb_right; + else + return m; + } + + return NULL; +} + +static int __read_proc_modules(void *arg, const char *name, u64 start) +{ + struct rb_root *modules = arg; + struct module_info *mi; + + mi = zalloc(sizeof(struct module_info)); + if (!mi) return -ENOMEM; - map->end = map->start + len; - map->pgoff = pgoff; + mi->name = strdup(name); + mi->start = start; - list_add(&map->node, &md->maps); + if (!mi->name) { + free(mi); + return -ENOMEM; + } + + add_module(mi, modules); + + return 0; +} + +static int read_proc_modules(const char *filename, struct rb_root *modules) +{ + if (symbol__restricted_filename(filename, "/proc/modules")) + return -1; + + if (modules__parse(filename, modules, __read_proc_modules)) { + delete_modules(modules); + return -1; + } return 0; } +static int do_validate_kcore_modules(const char *filename, struct map *map, + struct map_groups *kmaps) +{ + struct rb_root modules = RB_ROOT; + struct map *old_map; + int err; + + err = read_proc_modules(filename, &modules); + if (err) + return err; + + old_map = map_groups__first(kmaps, map->type); + while (old_map) { + struct map *next = map_groups__next(old_map); + struct module_info *mi; + + if (old_map == map || old_map->start == map->start) { + /* The kernel map */ + old_map = next; + continue; + } + + /* Module must be in memory at the same address */ + mi = find_module(old_map->dso->short_name, &modules); + if (!mi || mi->start != old_map->start) { + err = -EINVAL; + goto out; + } + + old_map = next; + } +out: + delete_modules(&modules); + return err; +} + /* - * If kallsyms is referenced by name then we look for kcore in the same + * If kallsyms is referenced by name then we look for filename in the same * directory. */ -static bool kcore_filename_from_kallsyms_filename(char *kcore_filename, - const char *kallsyms_filename) +static bool filename_from_kallsyms_filename(char *filename, + const char *base_name, + const char *kallsyms_filename) { char *name; - strcpy(kcore_filename, kallsyms_filename); - name = strrchr(kcore_filename, '/'); + strcpy(filename, kallsyms_filename); + name = strrchr(filename, '/'); if (!name) return false; - if (!strcmp(name, "/kallsyms")) { - strcpy(name, "/kcore"); + name += 1; + + if (!strcmp(name, "kallsyms")) { + strcpy(name, base_name); return true; } return false; } +static int validate_kcore_modules(const char *kallsyms_filename, + struct map *map) +{ + struct map_groups *kmaps = map__kmap(map)->kmaps; + char modules_filename[PATH_MAX]; + + if (!filename_from_kallsyms_filename(modules_filename, "modules", + kallsyms_filename)) + return -EINVAL; + + if (do_validate_kcore_modules(modules_filename, map, kmaps)) + return -EINVAL; + + return 0; +} + +struct kcore_mapfn_data { + struct dso *dso; + enum map_type type; + struct list_head maps; +}; + +static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) +{ + struct kcore_mapfn_data *md = data; + struct map *map; + + map = map__new2(start, md->dso, md->type); + if (map == NULL) + return -ENOMEM; + + map->end = map->start + len; + map->pgoff = pgoff; + + list_add(&map->node, &md->maps); + + return 0; +} + static int dso__load_kcore(struct dso *dso, struct map *map, const char *kallsyms_filename) { @@ -859,8 +1009,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map, if (map != machine->vmlinux_maps[map->type]) return -EINVAL; - if (!kcore_filename_from_kallsyms_filename(kcore_filename, - kallsyms_filename)) + if (!filename_from_kallsyms_filename(kcore_filename, "kcore", + kallsyms_filename)) + return -EINVAL; + + /* All modules must be present at their original addresses */ + if (validate_kcore_modules(kallsyms_filename, map)) return -EINVAL; md.dso = dso; -- cgit v1.2.2 From afba19d9dc8eba66ea26901708cf99354c637786 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 9 Oct 2013 15:01:12 +0300 Subject: perf symbols: Workaround objdump difficulties with kcore The objdump tool fails to annotate module symbols when looking at kcore. Workaround this by extracting object code from kcore and putting it in a temporary file for objdump to use instead. The temporary file is created to look like kcore but contains only the function being disassembled. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381320078-16497-3-git-send-email-adrian.hunter@intel.com [ Renamed 'index' to 'idx' to avoid shadowing string.h's 'index' in Fedora 12, Replace local with variable length with malloc/free to fix build in Fedora 12 ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 21 ++++ tools/perf/util/symbol-elf.c | 229 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/symbol-minimal.c | 9 ++ tools/perf/util/symbol.h | 14 +++ 4 files changed, 273 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d73e8008aada..882bb864cee0 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -879,6 +879,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) FILE *file; int err = 0; char symfs_filename[PATH_MAX]; + struct kcore_extract kce; + bool delete_extract = false; if (filename) { snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", @@ -940,6 +942,23 @@ fallback: pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); + if (dso__is_kcore(dso)) { + kce.kcore_filename = symfs_filename; + kce.addr = map__rip_2objdump(map, sym->start); + kce.offs = sym->start; + kce.len = sym->end + 1 - sym->start; + if (!kcore_extract__create(&kce)) { + delete_extract = true; + strlcpy(symfs_filename, kce.extract_filename, + sizeof(symfs_filename)); + if (free_filename) { + free(filename); + free_filename = false; + } + filename = symfs_filename; + } + } + snprintf(command, sizeof(command), "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 @@ -972,6 +991,8 @@ fallback: pclose(file); out_free_filename: + if (delete_extract) + kcore_extract__delete(&kce); if (free_filename) free(filename); return err; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index c37693052800..499c71d30225 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1018,6 +1018,235 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, return err; } +static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len) +{ + ssize_t r; + size_t n; + int err = -1; + char *buf = malloc(page_size); + + if (buf == NULL) + return -1; + + if (lseek(to, to_offs, SEEK_SET) != to_offs) + goto out; + + if (lseek(from, from_offs, SEEK_SET) != from_offs) + goto out; + + while (len) { + n = page_size; + if (len < n) + n = len; + /* Use read because mmap won't work on proc files */ + r = read(from, buf, n); + if (r < 0) + goto out; + if (!r) + break; + n = r; + r = write(to, buf, n); + if (r < 0) + goto out; + if ((size_t)r != n) + goto out; + len -= n; + } + + err = 0; +out: + free(buf); + return err; +} + +struct kcore { + int fd; + int elfclass; + Elf *elf; + GElf_Ehdr ehdr; +}; + +static int kcore__open(struct kcore *kcore, const char *filename) +{ + GElf_Ehdr *ehdr; + + kcore->fd = open(filename, O_RDONLY); + if (kcore->fd == -1) + return -1; + + kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL); + if (!kcore->elf) + goto out_close; + + kcore->elfclass = gelf_getclass(kcore->elf); + if (kcore->elfclass == ELFCLASSNONE) + goto out_end; + + ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); + if (!ehdr) + goto out_end; + + return 0; + +out_end: + elf_end(kcore->elf); +out_close: + close(kcore->fd); + return -1; +} + +static int kcore__init(struct kcore *kcore, char *filename, int elfclass, + bool temp) +{ + GElf_Ehdr *ehdr; + + kcore->elfclass = elfclass; + + if (temp) + kcore->fd = mkstemp(filename); + else + kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); + if (kcore->fd == -1) + return -1; + + kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL); + if (!kcore->elf) + goto out_close; + + if (!gelf_newehdr(kcore->elf, elfclass)) + goto out_end; + + ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); + if (!ehdr) + goto out_end; + + return 0; + +out_end: + elf_end(kcore->elf); +out_close: + close(kcore->fd); + unlink(filename); + return -1; +} + +static void kcore__close(struct kcore *kcore) +{ + elf_end(kcore->elf); + close(kcore->fd); +} + +static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count) +{ + GElf_Ehdr *ehdr = &to->ehdr; + GElf_Ehdr *kehdr = &from->ehdr; + + memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT); + ehdr->e_type = kehdr->e_type; + ehdr->e_machine = kehdr->e_machine; + ehdr->e_version = kehdr->e_version; + ehdr->e_entry = 0; + ehdr->e_shoff = 0; + ehdr->e_flags = kehdr->e_flags; + ehdr->e_phnum = count; + ehdr->e_shentsize = 0; + ehdr->e_shnum = 0; + ehdr->e_shstrndx = 0; + + if (from->elfclass == ELFCLASS32) { + ehdr->e_phoff = sizeof(Elf32_Ehdr); + ehdr->e_ehsize = sizeof(Elf32_Ehdr); + ehdr->e_phentsize = sizeof(Elf32_Phdr); + } else { + ehdr->e_phoff = sizeof(Elf64_Ehdr); + ehdr->e_ehsize = sizeof(Elf64_Ehdr); + ehdr->e_phentsize = sizeof(Elf64_Phdr); + } + + if (!gelf_update_ehdr(to->elf, ehdr)) + return -1; + + if (!gelf_newphdr(to->elf, count)) + return -1; + + return 0; +} + +static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset, + u64 addr, u64 len) +{ + GElf_Phdr gphdr; + GElf_Phdr *phdr; + + phdr = gelf_getphdr(kcore->elf, idx, &gphdr); + if (!phdr) + return -1; + + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R | PF_W | PF_X; + phdr->p_offset = offset; + phdr->p_vaddr = addr; + phdr->p_paddr = 0; + phdr->p_filesz = len; + phdr->p_memsz = len; + phdr->p_align = page_size; + + if (!gelf_update_phdr(kcore->elf, idx, phdr)) + return -1; + + return 0; +} + +static off_t kcore__write(struct kcore *kcore) +{ + return elf_update(kcore->elf, ELF_C_WRITE); +} + +int kcore_extract__create(struct kcore_extract *kce) +{ + struct kcore kcore; + struct kcore extract; + size_t count = 1; + int idx = 0, err = -1; + off_t offset = page_size, sz; + + if (kcore__open(&kcore, kce->kcore_filename)) + return -1; + + strcpy(kce->extract_filename, PERF_KCORE_EXTRACT); + if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true)) + goto out_kcore_close; + + if (kcore__copy_hdr(&kcore, &extract, count)) + goto out_extract_close; + + if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len)) + goto out_extract_close; + + sz = kcore__write(&extract); + if (sz < 0 || sz > offset) + goto out_extract_close; + + if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len)) + goto out_extract_close; + + err = 0; + +out_extract_close: + kcore__close(&extract); + if (err) + unlink(kce->extract_filename); +out_kcore_close: + kcore__close(&kcore); + + return err; +} + +void kcore_extract__delete(struct kcore_extract *kce) +{ + unlink(kce->extract_filename); +} + void symbol__elf_init(void) { elf_version(EV_CURRENT); diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 3a802c300fc5..928556d2508f 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -308,6 +308,15 @@ int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused, return -1; } +int kcore_extract__create(struct kcore_extract *kce __maybe_unused) +{ + return -1; +} + +void kcore_extract__delete(struct kcore_extract *kce __maybe_unused) +{ +} + void symbol__elf_init(void) { } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 2d3eb43ab9f9..fb107e13e925 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -256,4 +256,18 @@ typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data); int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, bool *is_64_bit); +#define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX" + +struct kcore_extract { + char *kcore_filename; + u64 addr; + u64 offs; + u64 len; + char extract_filename[sizeof(PERF_KCORE_EXTRACT)]; + int fd; +}; + +int kcore_extract__create(struct kcore_extract *kce); +void kcore_extract__delete(struct kcore_extract *kce); + #endif /* __PERF_SYMBOL */ -- cgit v1.2.2 From 4e987712740a3634c19a6fedaf12577b26775dc5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Oct 2013 13:43:38 +0300 Subject: perf symbols: Add map_groups__find_ams() Add a function to find a symbol using an ip that might be on a different map. Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Adrian Hunter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/map.c | 17 +++++++++++++++++ tools/perf/util/map.h | 4 ++++ 2 files changed, 21 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 17ee458a0870..9dea404de3fa 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -371,6 +371,23 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, return NULL; } +int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) +{ + if (ams->addr < ams->map->start || ams->addr > ams->map->end) { + if (ams->map->groups == NULL) + return -1; + ams->map = map_groups__find(ams->map->groups, ams->map->type, + ams->addr); + if (ams->map == NULL) + return -1; + } + + ams->al_addr = ams->map->map_ip(ams->map, ams->addr); + ams->sym = map__find_symbol(ams->map, ams->al_addr, filter); + + return ams->sym ? 0 : -1; +} + size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, int verbose, FILE *fp) { diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4886ca280536..0359b4a808f0 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -167,6 +167,10 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, struct map **mapp, symbol_filter_t filter); +struct addr_map_symbol; + +int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter); + static inline struct symbol *map_groups__find_function_by_name(struct map_groups *mg, const char *name, struct map **mapp, -- cgit v1.2.2 From 6e427ab02c8886ca6c9ecdbb318e68fe8f605469 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:40 +0300 Subject: perf annotate: Find kcore symbols on other maps Use the new map_groups__find_ams() method to find kcore symbols on other maps. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-4-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 882bb864cee0..cf6242c92ee2 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -825,20 +825,16 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, dl->ops.target.offset = dl->ops.target.addr - map__rip_2objdump(map, sym->start); - /* - * kcore has no symbols, so add the call target name if it is on the - * same map. - */ + /* kcore has no symbols, so add the call target name */ if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { - struct symbol *s; - u64 ip = dl->ops.target.addr; - - if (ip >= map->start && ip <= map->end) { - ip = map->map_ip(map, ip); - s = map__find_symbol(map, ip, NULL); - if (s && s->start == ip) - dl->ops.target.name = strdup(s->name); - } + struct addr_map_symbol target = { + .map = map, + .addr = dl->ops.target.addr, + }; + + if (!map_groups__find_ams(&target, NULL) && + target.sym->start == target.al_addr) + dl->ops.target.name = strdup(target.sym->name); } disasm__add(¬es->src->source, dl); -- cgit v1.2.2 From 9a17d7268d71674f0bbff6821f7d8e6dc0ece19a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:41 +0300 Subject: perf tools: Add copyfile_mode() Add a function to copy a file specifying the permissions to use for the created file. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-5-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 18 +++++++++++++----- tools/perf/util/util.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index ab71d6216803..8dc8cf39f4ed 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -55,17 +55,20 @@ int mkdir_p(char *path, mode_t mode) return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; } -static int slow_copyfile(const char *from, const char *to) +static int slow_copyfile(const char *from, const char *to, mode_t mode) { - int err = 0; + int err = -1; char *line = NULL; size_t n; FILE *from_fp = fopen(from, "r"), *to_fp; + mode_t old_umask; if (from_fp == NULL) goto out; + old_umask = umask(mode ^ 0777); to_fp = fopen(to, "w"); + umask(old_umask); if (to_fp == NULL) goto out_fclose_from; @@ -82,7 +85,7 @@ out: return err; } -int copyfile(const char *from, const char *to) +int copyfile_mode(const char *from, const char *to, mode_t mode) { int fromfd, tofd; struct stat st; @@ -93,13 +96,13 @@ int copyfile(const char *from, const char *to) goto out; if (st.st_size == 0) /* /proc? do it slowly... */ - return slow_copyfile(from, to); + return slow_copyfile(from, to, mode); fromfd = open(from, O_RDONLY); if (fromfd < 0) goto out; - tofd = creat(to, 0755); + tofd = creat(to, mode); if (tofd < 0) goto out_close_from; @@ -121,6 +124,11 @@ out: return err; } +int copyfile(const char *from, const char *to) +{ + return copyfile_mode(from, to, 0755); +} + unsigned long convert_unit(unsigned long value, char *unit) { *unit = ' '; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 1f06ba44cd53..42dfba70fbfc 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -243,6 +243,7 @@ static inline int sane_case(int x, int high) int mkdir_p(char *path, mode_t mode); int copyfile(const char *from, const char *to); +int copyfile_mode(const char *from, const char *to, mode_t mode); s64 perf_atoll(const char *str); char **argv_split(const char *str, int *argcp); -- cgit v1.2.2 From 0544d4225c52ca31ab2a55b22ddce1392d8f45a4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:43 +0300 Subject: perf symbols: Add ability to find kcore in build-id cache When no vmlinux is found, tools will use kallsyms and, if possible, kcore. Add the ability to find kcore in the build-id cache. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-7-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 147 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 103 insertions(+), 44 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b2f60ddc864f..76a9e933a7a5 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1401,6 +1401,105 @@ out: return err; } +static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) +{ + char kallsyms_filename[PATH_MAX]; + struct dirent *dent; + int ret = -1; + DIR *d; + + d = opendir(dir); + if (!d) + return -1; + + while (1) { + dent = readdir(d); + if (!dent) + break; + if (dent->d_type != DT_DIR) + continue; + scnprintf(kallsyms_filename, sizeof(kallsyms_filename), + "%s/%s/kallsyms", dir, dent->d_name); + if (!validate_kcore_modules(kallsyms_filename, map)) { + strlcpy(dir, kallsyms_filename, dir_sz); + ret = 0; + break; + } + } + + closedir(d); + + return ret; +} + +static char *dso__find_kallsyms(struct dso *dso, struct map *map) +{ + u8 host_build_id[BUILD_ID_SIZE]; + char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + bool is_host = false; + char path[PATH_MAX]; + + if (!dso->has_build_id) { + /* + * Last resort, if we don't have a build-id and couldn't find + * any vmlinux file, try the running kernel kallsyms table. + */ + goto proc_kallsyms; + } + + if (sysfs__read_build_id("/sys/kernel/notes", host_build_id, + sizeof(host_build_id)) == 0) + is_host = dso__build_id_equal(dso, host_build_id); + + build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + + /* Use /proc/kallsyms if possible */ + if (is_host) { + DIR *d; + int fd; + + /* If no cached kcore go with /proc/kallsyms */ + scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", + buildid_dir, sbuild_id); + d = opendir(path); + if (!d) + goto proc_kallsyms; + closedir(d); + + /* + * Do not check the build-id cache, until we know we cannot use + * /proc/kcore. + */ + fd = open("/proc/kcore", O_RDONLY); + if (fd != -1) { + close(fd); + /* If module maps match go with /proc/kallsyms */ + if (!validate_kcore_modules("/proc/kallsyms", map)) + goto proc_kallsyms; + } + + /* Find kallsyms in build-id cache with kcore */ + if (!find_matching_kcore(map, path, sizeof(path))) + return strdup(path); + + goto proc_kallsyms; + } + + scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", + buildid_dir, sbuild_id); + + if (access(path, F_OK)) { + pr_err("No kallsyms or vmlinux with build-id %s was found\n", + sbuild_id); + return NULL; + } + + return strdup(path); + +proc_kallsyms: + return strdup("/proc/kallsyms"); +} + static int dso__load_kernel_sym(struct dso *dso, struct map *map, symbol_filter_t filter) { @@ -1449,51 +1548,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, if (symbol_conf.symfs[0] != 0) return -1; - /* - * Say the kernel DSO was created when processing the build-id header table, - * we have a build-id, so check if it is the same as the running kernel, - * using it if it is. - */ - if (dso->has_build_id) { - u8 kallsyms_build_id[BUILD_ID_SIZE]; - char sbuild_id[BUILD_ID_SIZE * 2 + 1]; - - if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, - sizeof(kallsyms_build_id)) == 0) { - if (dso__build_id_equal(dso, kallsyms_build_id)) { - kallsyms_filename = "/proc/kallsyms"; - goto do_kallsyms; - } - } - /* - * Now look if we have it on the build-id cache in - * $HOME/.debug/[kernel.kallsyms]. - */ - build_id__sprintf(dso->build_id, sizeof(dso->build_id), - sbuild_id); - - if (asprintf(&kallsyms_allocated_filename, - "%s/.debug/[kernel.kallsyms]/%s", - getenv("HOME"), sbuild_id) == -1) { - pr_err("Not enough memory for kallsyms file lookup\n"); - return -1; - } - - kallsyms_filename = kallsyms_allocated_filename; + kallsyms_allocated_filename = dso__find_kallsyms(dso, map); + if (!kallsyms_allocated_filename) + return -1; - if (access(kallsyms_filename, F_OK)) { - pr_err("No kallsyms or vmlinux with build-id %s " - "was found\n", sbuild_id); - free(kallsyms_allocated_filename); - return -1; - } - } else { - /* - * Last resort, if we don't have a build-id and couldn't find - * any vmlinux file, try the running kernel kallsyms table. - */ - kallsyms_filename = "/proc/kallsyms"; - } + kallsyms_filename = kallsyms_allocated_filename; do_kallsyms: err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); -- cgit v1.2.2 From fc1b691d7651d9496e912de7e0fc73a5be3294af Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 16:57:29 +0300 Subject: perf buildid-cache: Add ability to add kcore to the cache kcore can be used to view the running kernel object code. However, kcore changes as modules are loaded and unloaded, and when the kernel decides to modify its own code. Consequently it is useful to create a copy of kcore at a particular time. Unlike vmlinux, kcore is not unique for a given build-id. And in addition, the kallsyms and modules files are also needed. The tool therefore creates a directory: ~/.debug/[kernel.kcore]// which contains: kcore, kallsyms and modules. Note that the copied kcore contains only code sections. See the kcore_copy() function for how that is determined. The tool will not make additional copies of kcore if there is already one with the same modules at the same addresses. Currently, perf tools will not look for kcore in the cache. That is addressed in another patch. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/525BF849.5030405@intel.com [ renamed 'index' to 'idx' to avoid shadowing string.h symbol in f12, use at least one member initializer when initializing a struct to zeros, also to fix the build on f12 ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 366 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/symbol-minimal.c | 6 + tools/perf/util/symbol.c | 41 +++++ tools/perf/util/symbol.h | 3 + 4 files changed, 416 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 499c71d30225..d6b8af3ce344 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1202,6 +1202,372 @@ static off_t kcore__write(struct kcore *kcore) return elf_update(kcore->elf, ELF_C_WRITE); } +struct phdr_data { + off_t offset; + u64 addr; + u64 len; +}; + +struct kcore_copy_info { + u64 stext; + u64 etext; + u64 first_symbol; + u64 last_symbol; + u64 first_module; + u64 last_module_symbol; + struct phdr_data kernel_map; + struct phdr_data modules_map; +}; + +static int kcore_copy__process_kallsyms(void *arg, const char *name, char type, + u64 start) +{ + struct kcore_copy_info *kci = arg; + + if (!symbol_type__is_a(type, MAP__FUNCTION)) + return 0; + + if (strchr(name, '[')) { + if (start > kci->last_module_symbol) + kci->last_module_symbol = start; + return 0; + } + + if (!kci->first_symbol || start < kci->first_symbol) + kci->first_symbol = start; + + if (!kci->last_symbol || start > kci->last_symbol) + kci->last_symbol = start; + + if (!strcmp(name, "_stext")) { + kci->stext = start; + return 0; + } + + if (!strcmp(name, "_etext")) { + kci->etext = start; + return 0; + } + + return 0; +} + +static int kcore_copy__parse_kallsyms(struct kcore_copy_info *kci, + const char *dir) +{ + char kallsyms_filename[PATH_MAX]; + + scnprintf(kallsyms_filename, PATH_MAX, "%s/kallsyms", dir); + + if (symbol__restricted_filename(kallsyms_filename, "/proc/kallsyms")) + return -1; + + if (kallsyms__parse(kallsyms_filename, kci, + kcore_copy__process_kallsyms) < 0) + return -1; + + return 0; +} + +static int kcore_copy__process_modules(void *arg, + const char *name __maybe_unused, + u64 start) +{ + struct kcore_copy_info *kci = arg; + + if (!kci->first_module || start < kci->first_module) + kci->first_module = start; + + return 0; +} + +static int kcore_copy__parse_modules(struct kcore_copy_info *kci, + const char *dir) +{ + char modules_filename[PATH_MAX]; + + scnprintf(modules_filename, PATH_MAX, "%s/modules", dir); + + if (symbol__restricted_filename(modules_filename, "/proc/modules")) + return -1; + + if (modules__parse(modules_filename, kci, + kcore_copy__process_modules) < 0) + return -1; + + return 0; +} + +static void kcore_copy__map(struct phdr_data *p, u64 start, u64 end, u64 pgoff, + u64 s, u64 e) +{ + if (p->addr || s < start || s >= end) + return; + + p->addr = s; + p->offset = (s - start) + pgoff; + p->len = e < end ? e - s : end - s; +} + +static int kcore_copy__read_map(u64 start, u64 len, u64 pgoff, void *data) +{ + struct kcore_copy_info *kci = data; + u64 end = start + len; + + kcore_copy__map(&kci->kernel_map, start, end, pgoff, kci->stext, + kci->etext); + + kcore_copy__map(&kci->modules_map, start, end, pgoff, kci->first_module, + kci->last_module_symbol); + + return 0; +} + +static int kcore_copy__read_maps(struct kcore_copy_info *kci, Elf *elf) +{ + if (elf_read_maps(elf, true, kcore_copy__read_map, kci) < 0) + return -1; + + return 0; +} + +static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir, + Elf *elf) +{ + if (kcore_copy__parse_kallsyms(kci, dir)) + return -1; + + if (kcore_copy__parse_modules(kci, dir)) + return -1; + + if (kci->stext) + kci->stext = round_down(kci->stext, page_size); + else + kci->stext = round_down(kci->first_symbol, page_size); + + if (kci->etext) { + kci->etext = round_up(kci->etext, page_size); + } else if (kci->last_symbol) { + kci->etext = round_up(kci->last_symbol, page_size); + kci->etext += page_size; + } + + kci->first_module = round_down(kci->first_module, page_size); + + if (kci->last_module_symbol) { + kci->last_module_symbol = round_up(kci->last_module_symbol, + page_size); + kci->last_module_symbol += page_size; + } + + if (!kci->stext || !kci->etext) + return -1; + + if (kci->first_module && !kci->last_module_symbol) + return -1; + + return kcore_copy__read_maps(kci, elf); +} + +static int kcore_copy__copy_file(const char *from_dir, const char *to_dir, + const char *name) +{ + char from_filename[PATH_MAX]; + char to_filename[PATH_MAX]; + + scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); + scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); + + return copyfile_mode(from_filename, to_filename, 0400); +} + +static int kcore_copy__unlink(const char *dir, const char *name) +{ + char filename[PATH_MAX]; + + scnprintf(filename, PATH_MAX, "%s/%s", dir, name); + + return unlink(filename); +} + +static int kcore_copy__compare_fds(int from, int to) +{ + char *buf_from; + char *buf_to; + ssize_t ret; + size_t len; + int err = -1; + + buf_from = malloc(page_size); + buf_to = malloc(page_size); + if (!buf_from || !buf_to) + goto out; + + while (1) { + /* Use read because mmap won't work on proc files */ + ret = read(from, buf_from, page_size); + if (ret < 0) + goto out; + + if (!ret) + break; + + len = ret; + + if (readn(to, buf_to, len) != (int)len) + goto out; + + if (memcmp(buf_from, buf_to, len)) + goto out; + } + + err = 0; +out: + free(buf_to); + free(buf_from); + return err; +} + +static int kcore_copy__compare_files(const char *from_filename, + const char *to_filename) +{ + int from, to, err = -1; + + from = open(from_filename, O_RDONLY); + if (from < 0) + return -1; + + to = open(to_filename, O_RDONLY); + if (to < 0) + goto out_close_from; + + err = kcore_copy__compare_fds(from, to); + + close(to); +out_close_from: + close(from); + return err; +} + +static int kcore_copy__compare_file(const char *from_dir, const char *to_dir, + const char *name) +{ + char from_filename[PATH_MAX]; + char to_filename[PATH_MAX]; + + scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name); + scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name); + + return kcore_copy__compare_files(from_filename, to_filename); +} + +/** + * kcore_copy - copy kallsyms, modules and kcore from one directory to another. + * @from_dir: from directory + * @to_dir: to directory + * + * This function copies kallsyms, modules and kcore files from one directory to + * another. kallsyms and modules are copied entirely. Only code segments are + * copied from kcore. It is assumed that two segments suffice: one for the + * kernel proper and one for all the modules. The code segments are determined + * from kallsyms and modules files. The kernel map starts at _stext or the + * lowest function symbol, and ends at _etext or the highest function symbol. + * The module map starts at the lowest module address and ends at the highest + * module symbol. Start addresses are rounded down to the nearest page. End + * addresses are rounded up to the nearest page. An extra page is added to the + * highest kernel symbol and highest module symbol to, hopefully, encompass that + * symbol too. Because it contains only code sections, the resulting kcore is + * unusual. One significant peculiarity is that the mapping (start -> pgoff) + * is not the same for the kernel map and the modules map. That happens because + * the data is copied adjacently whereas the original kcore has gaps. Finally, + * kallsyms and modules files are compared with their copies to check that + * modules have not been loaded or unloaded while the copies were taking place. + * + * Return: %0 on success, %-1 on failure. + */ +int kcore_copy(const char *from_dir, const char *to_dir) +{ + struct kcore kcore; + struct kcore extract; + size_t count = 2; + int idx = 0, err = -1; + off_t offset = page_size, sz, modules_offset = 0; + struct kcore_copy_info kci = { .stext = 0, }; + char kcore_filename[PATH_MAX]; + char extract_filename[PATH_MAX]; + + if (kcore_copy__copy_file(from_dir, to_dir, "kallsyms")) + return -1; + + if (kcore_copy__copy_file(from_dir, to_dir, "modules")) + goto out_unlink_kallsyms; + + scnprintf(kcore_filename, PATH_MAX, "%s/kcore", from_dir); + scnprintf(extract_filename, PATH_MAX, "%s/kcore", to_dir); + + if (kcore__open(&kcore, kcore_filename)) + goto out_unlink_modules; + + if (kcore_copy__calc_maps(&kci, from_dir, kcore.elf)) + goto out_kcore_close; + + if (kcore__init(&extract, extract_filename, kcore.elfclass, false)) + goto out_kcore_close; + + if (!kci.modules_map.addr) + count -= 1; + + if (kcore__copy_hdr(&kcore, &extract, count)) + goto out_extract_close; + + if (kcore__add_phdr(&extract, idx++, offset, kci.kernel_map.addr, + kci.kernel_map.len)) + goto out_extract_close; + + if (kci.modules_map.addr) { + modules_offset = offset + kci.kernel_map.len; + if (kcore__add_phdr(&extract, idx, modules_offset, + kci.modules_map.addr, kci.modules_map.len)) + goto out_extract_close; + } + + sz = kcore__write(&extract); + if (sz < 0 || sz > offset) + goto out_extract_close; + + if (copy_bytes(kcore.fd, kci.kernel_map.offset, extract.fd, offset, + kci.kernel_map.len)) + goto out_extract_close; + + if (modules_offset && copy_bytes(kcore.fd, kci.modules_map.offset, + extract.fd, modules_offset, + kci.modules_map.len)) + goto out_extract_close; + + if (kcore_copy__compare_file(from_dir, to_dir, "modules")) + goto out_extract_close; + + if (kcore_copy__compare_file(from_dir, to_dir, "kallsyms")) + goto out_extract_close; + + err = 0; + +out_extract_close: + kcore__close(&extract); + if (err) + unlink(extract_filename); +out_kcore_close: + kcore__close(&kcore); +out_unlink_modules: + if (err) + kcore_copy__unlink(to_dir, "modules"); +out_unlink_kallsyms: + if (err) + kcore_copy__unlink(to_dir, "kallsyms"); + + return err; +} + int kcore_extract__create(struct kcore_extract *kce) { struct kcore kcore; diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 928556d2508f..2d2dd0532b5a 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -317,6 +317,12 @@ void kcore_extract__delete(struct kcore_extract *kce __maybe_unused) { } +int kcore_copy(const char *from_dir __maybe_unused, + const char *to_dir __maybe_unused) +{ + return -1; +} + void symbol__elf_init(void) { } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 76a9e933a7a5..b66c1eefc313 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -893,6 +893,47 @@ static int read_proc_modules(const char *filename, struct rb_root *modules) return 0; } +int compare_proc_modules(const char *from, const char *to) +{ + struct rb_root from_modules = RB_ROOT; + struct rb_root to_modules = RB_ROOT; + struct rb_node *from_node, *to_node; + struct module_info *from_m, *to_m; + int ret = -1; + + if (read_proc_modules(from, &from_modules)) + return -1; + + if (read_proc_modules(to, &to_modules)) + goto out_delete_from; + + from_node = rb_first(&from_modules); + to_node = rb_first(&to_modules); + while (from_node) { + if (!to_node) + break; + + from_m = rb_entry(from_node, struct module_info, rb_node); + to_m = rb_entry(to_node, struct module_info, rb_node); + + if (from_m->start != to_m->start || + strcmp(from_m->name, to_m->name)) + break; + + from_node = rb_next(from_node); + to_node = rb_next(to_node); + } + + if (!from_node && !to_node) + ret = 0; + + delete_modules(&to_modules); +out_delete_from: + delete_modules(&from_modules); + + return ret; +} + static int do_validate_kcore_modules(const char *filename, struct map *map, struct map_groups *kmaps) { diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index fb107e13e925..07de8fea2f48 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -270,4 +270,7 @@ struct kcore_extract { int kcore_extract__create(struct kcore_extract *kce); void kcore_extract__delete(struct kcore_extract *kce); +int kcore_copy(const char *from_dir, const char *to_dir); +int compare_proc_modules(const char *from, const char *to); + #endif /* __PERF_SYMBOL */ -- cgit v1.2.2 From 1d5077bdd9a10c4297cded139989bb9ee2998a6c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Oct 2013 13:43:44 +0300 Subject: perf annotate: Another fix for annotate_browser__callq() The target address is provided by objdump and is not necessary a memory address. Add a helper to get the correct address. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381747424-3557-8-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/map.c | 31 ++++++++++++++++++++++++++++++- tools/perf/util/map.h | 3 +++ 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 9dea404de3fa..ef5bc913ca7a 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -252,10 +252,16 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp) return fprintf(fp, "%s", dsoname); } -/* +/** + * map__rip_2objdump - convert symbol start address to objdump address. + * @map: memory map + * @rip: symbol start address + * * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. * map->dso->adjust_symbols==1 for ET_EXEC-like cases except ET_REL which is * relative to section start. + * + * Return: Address suitable for passing to "objdump --start-address=" */ u64 map__rip_2objdump(struct map *map, u64 rip) { @@ -268,6 +274,29 @@ u64 map__rip_2objdump(struct map *map, u64 rip) return map->unmap_ip(map, rip); } +/** + * map__objdump_2mem - convert objdump address to a memory address. + * @map: memory map + * @ip: objdump address + * + * Closely related to map__rip_2objdump(), this function takes an address from + * objdump and converts it to a memory address. Note this assumes that @map + * contains the address. To be sure the result is valid, check it forwards + * e.g. map__rip_2objdump(map->map_ip(map, map__objdump_2mem(map, ip))) == ip + * + * Return: Memory address. + */ +u64 map__objdump_2mem(struct map *map, u64 ip) +{ + if (!map->dso->adjust_symbols) + return map->unmap_ip(map, ip); + + if (map->dso->rel) + return map->unmap_ip(map, ip + map->pgoff); + + return ip; +} + void map_groups__init(struct map_groups *mg) { int i; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 0359b4a808f0..e4e259c3ba16 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -84,6 +84,9 @@ static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip) /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ u64 map__rip_2objdump(struct map *map, u64 rip); +/* objdump address -> memory address */ +u64 map__objdump_2mem(struct map *map, u64 ip); + struct symbol; typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); -- cgit v1.2.2 From d4f74eb89199dc7bde5579783e9188841e1271e3 Mon Sep 17 00:00:00 2001 From: Chenggang Qin Date: Fri, 11 Oct 2013 08:27:59 +0800 Subject: perf symbols: Fix a memory leak due to symbol__delete not being used In function symbols__fixup_duplicate(), while duplicated symbols are found, only the rb_node is removed from the tree. The symbol structures themself are ignored. Then, these memory areas are lost. Signed-off-by: Chenggang Qin Acked-by: Namhyung Kim Cc: Andrew Morton Cc: Arjan van de Ven Cc: David Ahern Cc: Ingo Molnar Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Wu Fengguang Cc: Yanmin Zhang Link: http://lkml.kernel.org/r/1381451279-4109-3-git-send-email-chenggang.qin@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b66c1eefc313..c0c36965fff0 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -160,10 +160,12 @@ again: if (choose_best_symbol(curr, next) == SYMBOL_A) { rb_erase(&next->rb_node, symbols); + symbol__delete(next); goto again; } else { nd = rb_next(&curr->rb_node); rb_erase(&curr->rb_node, symbols); + symbol__delete(curr); } } } -- cgit v1.2.2 From 784f3390f9bd900adfb3b0373615e105a0d9749a Mon Sep 17 00:00:00 2001 From: Chenggang Qin Date: Fri, 11 Oct 2013 08:27:57 +0800 Subject: perf symbols: Fix a mmap and munmap mismatched bug In function filename__read_debuglink(), while the ELF file is opend and mmapped in elf_begin(), but if this file is considered to not be usable during the following code, we will goto the close(fd) directly. The elf_end() is skipped. So, the mmaped ELF file cannot be munmapped. The mmapped areas exist during the life of perf. This is a memory leak. This patch fixed this bug. Reviewed-by: Namhyung Kim Signed-off-by: Chenggang Qin Cc: Andrew Morton Cc: Arjan van de Ven Cc: Chenggang Qin Cc: David Ahern Cc: Ingo Molnar Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Wu Fengguang Cc: Yanmin Zhang Link: http://lkml.kernel.org/r/1381451279-4109-1-git-send-email-chenggang.qin@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index d6b8af3ce344..eed0b96302af 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -487,27 +487,27 @@ int filename__read_debuglink(const char *filename, char *debuglink, ek = elf_kind(elf); if (ek != ELF_K_ELF) - goto out_close; + goto out_elf_end; if (gelf_getehdr(elf, &ehdr) == NULL) { pr_err("%s: cannot get elf header.\n", __func__); - goto out_close; + goto out_elf_end; } sec = elf_section_by_name(elf, &ehdr, &shdr, ".gnu_debuglink", NULL); if (sec == NULL) - goto out_close; + goto out_elf_end; data = elf_getdata(sec, NULL); if (data == NULL) - goto out_close; + goto out_elf_end; /* the start of this section is a zero-terminated string */ strncpy(debuglink, data->d_buf, size); +out_elf_end: elf_end(elf); - out_close: close(fd); out: -- cgit v1.2.2 From 6650b181cc0b0c4218ebff9b0c368a2e56287907 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Oct 2013 18:25:12 -0300 Subject: perf scripting perl: Fix build error on Fedora 12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cast __u64 to u64 to silence this warning on older distros, such as Fedora 12: CC /tmp/build/perf/util/scripting-engines/trace-event-perl.o cc1: warnings being treated as errors util/scripting-engines/trace-event-perl.c: In function ‘perl_process_tracepoint’: util/scripting-engines/trace-event-perl.c:285: error: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘__u64’ make[1]: *** [/tmp/build/perf/util/scripting-engines/trace-event-perl.o] Error 1 make: *** [install] Error 2 make: Leaving directory `/home/acme/git/linux/tools/perf' [acme@fedora12 linux]$ Reported-by: Waiman Long Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi Cc: Waiman Long Link: http://lkml.kernel.org/n/tip-nlxofdqcdjfm0w9o6bgq4kqv@git.kernel.org Link: http://lkml.kernel.org/r/1381265120-58532-1-git-send-email-Waiman.Long@hp.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/scripting-engines/trace-event-perl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index a85e4ae5f3ac..c0c9795c4f02 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, event = find_cache_event(evsel); if (!event) - die("ug! no event found for type %" PRIu64, evsel->attr.config); + die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); pid = raw_field_value(event, "common_pid", data); -- cgit v1.2.2 From 6ef068cb8e77784431b6c80adc49d0b0a6a5df66 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2013 12:07:58 -0300 Subject: perf evlist: Introduce perf_evlist__strerror_tp method Out of 'perf trace', should be used by other tools that uses tracepoints. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Ramkumar Ramachandra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-lyvtxhchz4ga8fwht15x8wou@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 27 +++++++++++++++++++++++++++ tools/perf/util/evlist.h | 2 ++ 2 files changed, 29 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index cb9523f50a37..6737420891cd 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1126,3 +1126,30 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) return printed + fprintf(fp, "\n");; } + +int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, + int err, char *buf, size_t size) +{ + char sbuf[128]; + + switch (err) { + case ENOENT: + scnprintf(buf, size, "%s", + "Error:\tUnable to find debugfs\n" + "Hint:\tWas your kernel was compiled with debugfs support?\n" + "Hint:\tIs the debugfs filesystem mounted?\n" + "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); + break; + case EACCES: + scnprintf(buf, size, + "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" + "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", + debugfs_mountpoint, debugfs_mountpoint); + break; + default: + scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); + break; + } + + return 0; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 722618f84c53..386de1036442 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -168,6 +168,8 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); +int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); + static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) { struct perf_event_mmap_page *pc = mm->base; -- cgit v1.2.2 From 97a07f10c38064dea492794c99445f6260afcdc1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2013 16:33:43 -0300 Subject: perf tools: Introduce filename__read_int helper Just opens a file and calls atoi() in at most its first 64 bytes. To read things like /proc/sys/kernel/perf_event_paranoid. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-669q04c5tou5pnt8jtiz6y2r@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 17 +++++++++++++++++ tools/perf/util/util.h | 2 ++ 2 files changed, 19 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 8dc8cf39f4ed..c25e57b3acb2 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -394,3 +394,20 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) return (unsigned long) -1; } + +int filename__read_int(const char *filename, int *value) +{ + char line[64]; + int fd = open(filename, O_RDONLY), err = -1; + + if (fd < 0) + return -1; + + if (read(fd, line, sizeof(line)) > 0) { + *value = atoi(line); + err = 0; + } + + close(fd); + return err; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 42dfba70fbfc..c8f362daba87 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -305,4 +305,6 @@ struct dso; char *get_srcline(struct dso *dso, unsigned long addr); void free_srcline(char *srcline); + +int filename__read_int(const char *filename, int *value); #endif /* GIT_COMPAT_UTIL_H */ -- cgit v1.2.2 From a8f23d8f8af43d49cd3331681913b76e4951e1a4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2013 17:38:29 -0300 Subject: perf trace: Improve messages related to /proc/sys/kernel/perf_event_paranoid kernel/events/core.c has: /* * perf event paranoia level: * -1 - not paranoid at all * 0 - disallow raw tracepoint access for unpriv * 1 - disallow cpu events for unpriv * 2 - disallow kernel profiling for unpriv */ int sysctl_perf_event_paranoid __read_mostly = 1; So, with the default being 1, a non-root user can trace his stuff: [acme@zoo ~]$ cat /proc/sys/kernel/perf_event_paranoid 1 [acme@zoo ~]$ yes > /dev/null & [1] 15338 [acme@zoo ~]$ trace -p 15338 | head -5 0.005 ( 0.005 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 0.045 ( 0.001 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 0.085 ( 0.001 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 0.125 ( 0.001 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 0.165 ( 0.001 ms): write(fd: 1, buf: 0x7fe6db765000, count: 4096 ) = 4096 [acme@zoo ~]$ [acme@zoo ~]$ trace --duration 1 sleep 1 1002.148 (1001.218 ms): nanosleep(rqtp: 0x7fff46c79250 ) = 0 [acme@zoo ~]$ [acme@zoo ~]$ trace -- usleep 1 | tail -5 0.905 ( 0.002 ms): brk( ) = 0x1c82000 0.910 ( 0.003 ms): brk(brk: 0x1ca3000 ) = 0x1ca3000 0.913 ( 0.001 ms): brk( ) = 0x1ca3000 0.990 ( 0.059 ms): nanosleep(rqtp: 0x7fffe31a3280 ) = 0 0.995 ( 0.000 ms): exit_group( [acme@zoo ~]$ But can't do system wide tracing: [acme@zoo ~]$ trace Error: Operation not permitted. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 1. [acme@zoo ~]$ [acme@zoo ~]$ trace --cpu 0 Error: Operation not permitted. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 1. [acme@zoo ~]$ If the paranoid level is >= 2, i.e. turn this perf stuff off for !root users: [acme@zoo ~]$ sudo sh -c 'echo 2 > /proc/sys/kernel/perf_event_paranoid' [acme@zoo ~]$ cat /proc/sys/kernel/perf_event_paranoid 2 [acme@zoo ~]$ [acme@zoo ~]$ trace usleep 1 Error: Permission denied. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For your workloads it needs to be <= 1 Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 2. [acme@zoo ~]$ [acme@zoo ~]$ trace Error: Permission denied. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For your workloads it needs to be <= 1 Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 2. [acme@zoo ~]$ [acme@zoo ~]$ trace --cpu 1 Error: Permission denied. Hint: Check /proc/sys/kernel/perf_event_paranoid setting. Hint: For your workloads it needs to be <= 1 Hint: For system wide tracing it needs to be set to -1. Hint: The current value is 2. [acme@zoo ~]$ If the user manages to get what he/she wants, convincing root not to be paranoid at all... [root@zoo ~]# echo -1 > /proc/sys/kernel/perf_event_paranoid [root@zoo ~]# cat /proc/sys/kernel/perf_event_paranoid -1 [root@zoo ~]# [acme@zoo ~]$ ps -eo user,pid,comm | grep Xorg root 729 Xorg [acme@zoo ~]$ [acme@zoo ~]$ trace -a --duration 0.001 -e \!select,ioctl,writev | grep Xorg | head -5 23.143 ( 0.003 ms): Xorg/729 setitimer(which: REAL, value: 0x7fffaadf16e0 ) = 0 23.152 ( 0.004 ms): Xorg/729 read(fd: 31, buf: 0x2544af0, count: 4096 ) = 8 23.161 ( 0.002 ms): Xorg/729 read(fd: 31, buf: 0x2544af0, count: 4096 ) = -1 EAGAIN Resource temporarily unavailable 23.175 ( 0.002 ms): Xorg/729 setitimer(which: REAL, value: 0x7fffaadf16e0 ) = 0 23.235 ( 0.002 ms): Xorg/729 setitimer(which: REAL, value: 0x7fffaadf16e0 ) = 0 [acme@zoo ~]$ Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-di28olfwd28rvkox7v3hqhu1@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 36 ++++++++++++++++++++++++++++++++++++ tools/perf/util/evlist.h | 1 + 2 files changed, 37 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 6737420891cd..0b5425b4d8d6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1153,3 +1153,39 @@ int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, return 0; } + +int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, + int err, char *buf, size_t size) +{ + int printed, value; + char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); + + switch (err) { + case EACCES: + case EPERM: + printed = scnprintf(buf, size, + "Error:\t%s.\n" + "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); + + if (filename__read_int("/proc/sys/kernel/perf_event_paranoid", &value)) + break; + + printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); + + if (value >= 2) { + printed += scnprintf(buf + printed, size - printed, + "For your workloads it needs to be <= 1\nHint:\t"); + } + printed += scnprintf(buf + printed, size - printed, + "For system wide tracing it needs to be set to -1"); + + printed += scnprintf(buf + printed, size - printed, + ".\nHint:\tThe current value is %d.", value); + break; + default: + scnprintf(buf, size, "%s", emsg); + break; + } + + return 0; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 386de1036442..7f8f1aeb9cfe 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -169,6 +169,7 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); +int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) { -- cgit v1.2.2 From 40d54ec2f77edc52340dcae236aaabe8c3cc3a07 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:28:58 +0300 Subject: perf evsel: Add missing 'mmap2' from debug print The struct perf_event_attr now has a 'mmap2' member. Add it to perf_event_attr__fprintf(). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index bfebc1ea3c51..291b18ac236a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -986,6 +986,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) ret += PRINT_ATTR2(exclude_host, exclude_guest); ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, "excl.callchain_user", exclude_callchain_user); + ret += PRINT_ATTR_U32(mmap2); ret += PRINT_ATTR_U32(wakeup_events); ret += PRINT_ATTR_U32(wakeup_watermark); -- cgit v1.2.2 From dd44bc6be05e4a948124053c8105cfa581177554 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:01 +0300 Subject: perf evsel: Add missing decrement in id sample parsing The final array decrement in id sample parsing is missing, which may trip up the next person adding a sample format, so add it in. Signed-off-by: Adrian Hunter Acked-by: Jiri Olsa Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-5-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 291b18ac236a..ec0cc1e21c62 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1218,6 +1218,7 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, sample->pid = u.val32[0]; sample->tid = u.val32[1]; + array--; } return 0; -- cgit v1.2.2 From 8c16b649606ff9f6d742ad6f71c76fc0ee996c8e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:02 +0300 Subject: perf session: Add missing sample flush for piped events Piped events can be sorted so a final flush is needed. Add that and remove a redundant 'err = 0'. Signed-off-by: Adrian Hunter Reviewed-by: Jiri Olsa Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-6-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d1e449534b33..d51e62db96a7 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1263,7 +1263,9 @@ more: if (!session_done()) goto more; done: - err = 0; + /* do the final flush for ordered samples */ + self->ordered_samples.next_flush = ULLONG_MAX; + err = flush_sample_queue(self, tool); out_err: free(buf); perf_session__warn_about_errors(self, tool); @@ -1392,13 +1394,13 @@ more: "Processing events..."); } - err = 0; if (session_done()) - goto out_err; + goto out; if (file_pos < file_size) goto more; +out: /* do the final flush for ordered samples */ session->ordered_samples.next_flush = ULLONG_MAX; err = flush_sample_queue(session, tool); -- cgit v1.2.2 From 7db5952846dfa015d74e64b5e8656acac979490b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:03 +0300 Subject: perf session: Add missing members to perf_event__attr_swap() The perf_event__attr_swap() method needs to swap all members of struct perf_event_attr. Add missing ones. Signed-off-by: Adrian Hunter Acked-by: Jiri Olsa Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-7-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d51e62db96a7..d4559ca08ca6 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -453,6 +453,9 @@ void perf_event__attr_swap(struct perf_event_attr *attr) attr->bp_type = bswap_32(attr->bp_type); attr->bp_addr = bswap_64(attr->bp_addr); attr->bp_len = bswap_64(attr->bp_len); + attr->branch_sample_type = bswap_64(attr->branch_sample_type); + attr->sample_regs_user = bswap_64(attr->sample_regs_user); + attr->sample_stack_user = bswap_32(attr->sample_stack_user); swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); } -- cgit v1.2.2 From 2af68ef50c6afc1632edc984e9c834545d90f597 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:07 +0300 Subject: perf evlist: Fix 32-bit build error util/evlist.c: In function 'perf_evlist__mmap': util/evlist.c:772:2: error: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'size_t' [-Werror=format] cc1: all warnings being treated as errors Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-11-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 0b5425b4d8d6..07ba0a43afd2 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -769,7 +769,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, evlist->overwrite = overwrite; evlist->mmap_len = perf_evlist__mmap_size(pages); - pr_debug("mmap size %luB\n", evlist->mmap_len); + pr_debug("mmap size %zuB\n", evlist->mmap_len); mask = evlist->mmap_len - page_size - 1; list_for_each_entry(evsel, &evlist->entries, node) { -- cgit v1.2.2 From c83fa7f2549cb60bc4d429de31096546f659ce6d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:12 +0300 Subject: perf evlist: Fix perf_evlist__mmap comments Put the comments into the correct kernel-doc format and correct reference to perf_evlist__read_on_cpu() which should be perf_evlist__mmap_read(). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-16-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 07ba0a43afd2..acef94a5ff9e 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -738,20 +738,17 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, return 0; } -/** perf_evlist__mmap - Create per cpu maps to receive events - * - * @evlist - list of events - * @pages - map length in pages - * @overwrite - overwrite older events? - * - * If overwrite is false the user needs to signal event consuption using: - * - * struct perf_mmap *m = &evlist->mmap[cpu]; - * unsigned int head = perf_mmap__read_head(m); +/** + * perf_evlist__mmap - Create mmaps to receive events. + * @evlist: list of events + * @pages: map length in pages + * @overwrite: overwrite older events? * - * perf_mmap__write_tail(m, head) + * If @overwrite is %false the user needs to signal event consumption using + * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this + * automatically. * - * Using perf_evlist__read_on_cpu does this automatically. + * Return: %0 on success, negative error code otherwise. */ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, bool overwrite) -- cgit v1.2.2 From 04e213148c434544e3aa88d227b5375fd375738b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 18 Oct 2013 15:29:13 +0300 Subject: perf evlist: Factor out duplicated mmap code The same code is used in perf_evlist__mmap_per_cpu() and perf_evlist__mmap_per_thread(). Factor it out into a separate function perf_evlist__mmap_per_evsel(). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382099356-4918-17-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 73 ++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 37 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index acef94a5ff9e..85c4c80bcac8 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -608,9 +608,36 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, return 0; } -static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) +static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, + int prot, int mask, int cpu, int thread, + int *output) { struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) { + int fd = FD(evsel, cpu, thread); + + if (*output == -1) { + *output = fd; + if (__perf_evlist__mmap(evlist, idx, prot, mask, + *output) < 0) + return -1; + } else { + if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) + return -1; + } + + if ((evsel->attr.read_format & PERF_FORMAT_ID) && + perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) + return -1; + } + + return 0; +} + +static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, + int mask) +{ int cpu, thread; int nr_cpus = cpu_map__nr(evlist->cpus); int nr_threads = thread_map__nr(evlist->threads); @@ -620,23 +647,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m int output = -1; for (thread = 0; thread < nr_threads; thread++) { - list_for_each_entry(evsel, &evlist->entries, node) { - int fd = FD(evsel, cpu, thread); - - if (output == -1) { - output = fd; - if (__perf_evlist__mmap(evlist, cpu, - prot, mask, output) < 0) - goto out_unmap; - } else { - if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) - goto out_unmap; - } - - if ((evsel->attr.read_format & PERF_FORMAT_ID) && - perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) - goto out_unmap; - } + if (perf_evlist__mmap_per_evsel(evlist, cpu, prot, mask, + cpu, thread, &output)) + goto out_unmap; } } @@ -648,9 +661,9 @@ out_unmap: return -1; } -static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) +static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, + int mask) { - struct perf_evsel *evsel; int thread; int nr_threads = thread_map__nr(evlist->threads); @@ -658,23 +671,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in for (thread = 0; thread < nr_threads; thread++) { int output = -1; - list_for_each_entry(evsel, &evlist->entries, node) { - int fd = FD(evsel, 0, thread); - - if (output == -1) { - output = fd; - if (__perf_evlist__mmap(evlist, thread, - prot, mask, output) < 0) - goto out_unmap; - } else { - if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) - goto out_unmap; - } - - if ((evsel->attr.read_format & PERF_FORMAT_ID) && - perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0) - goto out_unmap; - } + if (perf_evlist__mmap_per_evsel(evlist, thread, prot, mask, 0, + thread, &output)) + goto out_unmap; } return 0; -- cgit v1.2.2 From f11cfc6f294dbd83b0d58037404df2bd16066238 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 15 Oct 2013 23:07:27 +0300 Subject: perf list: Show error if tracepoints not available Tracepoints are not visible in "perf list" on Fedora 19 because regular users have no permission to /sys/kernel/debug by default. Show an error message so that the user knows about it instead of assuming that tracepoints are not supported on the system. Signed-off-by: Pekka Enberg Acked-by: Ingo Molnar Cc: Ingo Molnar Link: http://lkml.kernel.org/r/1381867647-8594-1-git-send-email-penberg@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 98125319b158..c90e55cf7e82 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -998,8 +998,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, char evt_path[MAXPATHLEN]; char dir_path[MAXPATHLEN]; - if (debugfs_valid_mountpoint(tracing_events_path)) + if (debugfs_valid_mountpoint(tracing_events_path)) { + printf(" [ Tracepoints not available: %s ]\n", strerror(errno)); return; + } sys_dir = opendir(tracing_events_path); if (!sys_dir) -- cgit v1.2.2 From e369517ce5f796945c6af047b4e8b1d650e03458 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 11 Oct 2013 14:15:36 +0900 Subject: perf callchain: Convert children list to rbtree Current collapse stage has a scalability problem which can be reproduced easily with a parallel kernel build. This is because it needs to traverse every children of callchains linearly during the collapse/merge stage. Converting it to a rbtree reduced the overhead significantly. On my 400MB perf.data file which recorded with make -j32 kernel build: $ time perf --no-pager report --stdio > /dev/null before: real 6m22.073s user 6m18.683s sys 0m0.706s after: real 0m20.780s user 0m19.962s sys 0m0.689s During the perf report the overhead on append_chain_children went down from 96.69% to 18.16%: - 18.16% perf perf [.] append_chain_children - append_chain_children - 77.48% append_chain_children + 69.79% merge_chain_branch - 22.96% append_chain_children + 67.44% merge_chain_branch + 30.15% append_chain_children + 2.41% callchain_append + 7.25% callchain_append + 12.26% callchain_append + 10.22% merge_chain_branch + 11.58% perf perf [.] dso__find_symbol + 8.02% perf perf [.] sort__comm_cmp + 5.48% perf libc-2.17.so [.] malloc_consolidate Reported-by: Linus Torvalds Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Linus Torvalds Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381468543-25334-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 147 +++++++++++++++++++++++++++++++++----------- tools/perf/util/callchain.h | 11 ++-- 2 files changed, 116 insertions(+), 42 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 482f68081cd8..e3970e3eaacf 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -21,12 +21,6 @@ __thread struct callchain_cursor callchain_cursor; -#define chain_for_each_child(child, parent) \ - list_for_each_entry(child, &parent->children, siblings) - -#define chain_for_each_child_safe(child, next, parent) \ - list_for_each_entry_safe(child, next, &parent->children, siblings) - static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, enum chain_mode mode) @@ -71,10 +65,16 @@ static void __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, u64 min_hit) { + struct rb_node *n; struct callchain_node *child; - chain_for_each_child(child, node) + n = rb_first(&node->rb_root_in); + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + n = rb_next(n); + __sort_chain_flat(rb_root, child, min_hit); + } if (node->hit && node->hit >= min_hit) rb_insert_callchain(rb_root, node, CHAIN_FLAT); @@ -94,11 +94,16 @@ sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, static void __sort_chain_graph_abs(struct callchain_node *node, u64 min_hit) { + struct rb_node *n; struct callchain_node *child; node->rb_root = RB_ROOT; + n = rb_first(&node->rb_root_in); + + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + n = rb_next(n); - chain_for_each_child(child, node) { __sort_chain_graph_abs(child, min_hit); if (callchain_cumul_hits(child) >= min_hit) rb_insert_callchain(&node->rb_root, child, @@ -117,13 +122,18 @@ sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, static void __sort_chain_graph_rel(struct callchain_node *node, double min_percent) { + struct rb_node *n; struct callchain_node *child; u64 min_hit; node->rb_root = RB_ROOT; min_hit = ceil(node->children_hit * min_percent); - chain_for_each_child(child, node) { + n = rb_first(&node->rb_root_in); + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + n = rb_next(n); + __sort_chain_graph_rel(child, min_percent); if (callchain_cumul_hits(child) >= min_hit) rb_insert_callchain(&node->rb_root, child, @@ -173,19 +183,26 @@ create_child(struct callchain_node *parent, bool inherit_children) return NULL; } new->parent = parent; - INIT_LIST_HEAD(&new->children); INIT_LIST_HEAD(&new->val); if (inherit_children) { - struct callchain_node *next; + struct rb_node *n; + struct callchain_node *child; + + new->rb_root_in = parent->rb_root_in; + parent->rb_root_in = RB_ROOT; - list_splice(&parent->children, &new->children); - INIT_LIST_HEAD(&parent->children); + n = rb_first(&new->rb_root_in); + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + child->parent = new; + n = rb_next(n); + } - chain_for_each_child(next, new) - next->parent = new; + /* make it the first child */ + rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node); + rb_insert_color(&new->rb_node_in, &parent->rb_root_in); } - list_add_tail(&new->siblings, &parent->children); return new; } @@ -223,7 +240,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) } } -static void +static struct callchain_node * add_child(struct callchain_node *parent, struct callchain_cursor *cursor, u64 period) @@ -235,6 +252,19 @@ add_child(struct callchain_node *parent, new->children_hit = 0; new->hit = period; + return new; +} + +static s64 match_chain(struct callchain_cursor_node *node, + struct callchain_list *cnode) +{ + struct symbol *sym = node->sym; + + if (cnode->ms.sym && sym && + callchain_param.key == CCKEY_FUNCTION) + return cnode->ms.sym->start - sym->start; + else + return cnode->ip - node->ip; } /* @@ -272,9 +302,33 @@ split_add_child(struct callchain_node *parent, /* create a new child for the new branch if any */ if (idx_total < cursor->nr) { + struct callchain_node *first; + struct callchain_list *cnode; + struct callchain_cursor_node *node; + struct rb_node *p, **pp; + parent->hit = 0; - add_child(parent, cursor, period); parent->children_hit += period; + + node = callchain_cursor_current(cursor); + new = add_child(parent, cursor, period); + + /* + * This is second child since we moved parent's children + * to new (first) child above. + */ + p = parent->rb_root_in.rb_node; + first = rb_entry(p, struct callchain_node, rb_node_in); + cnode = list_first_entry(&first->val, struct callchain_list, + list); + + if (match_chain(node, cnode) < 0) + pp = &p->rb_left; + else + pp = &p->rb_right; + + rb_link_node(&new->rb_node_in, p, pp); + rb_insert_color(&new->rb_node_in, &parent->rb_root_in); } else { parent->hit = period; } @@ -291,16 +345,40 @@ append_chain_children(struct callchain_node *root, u64 period) { struct callchain_node *rnode; + struct callchain_cursor_node *node; + struct rb_node **p = &root->rb_root_in.rb_node; + struct rb_node *parent = NULL; + + node = callchain_cursor_current(cursor); + if (!node) + return; /* lookup in childrens */ - chain_for_each_child(rnode, root) { - unsigned int ret = append_chain(rnode, cursor, period); + while (*p) { + s64 ret; + struct callchain_list *cnode; - if (!ret) + parent = *p; + rnode = rb_entry(parent, struct callchain_node, rb_node_in); + cnode = list_first_entry(&rnode->val, struct callchain_list, + list); + + /* just check first entry */ + ret = match_chain(node, cnode); + if (ret == 0) { + append_chain(rnode, cursor, period); goto inc_children_hit; + } + + if (ret < 0) + p = &parent->rb_left; + else + p = &parent->rb_right; } /* nothing in children, add to the current node */ - add_child(root, cursor, period); + rnode = add_child(root, cursor, period); + rb_link_node(&rnode->rb_node_in, parent, p); + rb_insert_color(&rnode->rb_node_in, &root->rb_root_in); inc_children_hit: root->children_hit += period; @@ -325,28 +403,20 @@ append_chain(struct callchain_node *root, */ list_for_each_entry(cnode, &root->val, list) { struct callchain_cursor_node *node; - struct symbol *sym; node = callchain_cursor_current(cursor); if (!node) break; - sym = node->sym; - - if (cnode->ms.sym && sym && - callchain_param.key == CCKEY_FUNCTION) { - if (cnode->ms.sym->start != sym->start) - break; - } else if (cnode->ip != node->ip) + if (match_chain(node, cnode) != 0) break; - if (!found) - found = true; + found = true; callchain_cursor_advance(cursor); } - /* matches not, relay on the parent */ + /* matches not, relay no the parent */ if (!found) { cursor->curr = curr_snap; cursor->pos = start; @@ -395,8 +465,9 @@ merge_chain_branch(struct callchain_cursor *cursor, struct callchain_node *dst, struct callchain_node *src) { struct callchain_cursor_node **old_last = cursor->last; - struct callchain_node *child, *next_child; + struct callchain_node *child; struct callchain_list *list, *next_list; + struct rb_node *n; int old_pos = cursor->nr; int err = 0; @@ -412,12 +483,16 @@ merge_chain_branch(struct callchain_cursor *cursor, append_chain_children(dst, cursor, src->hit); } - chain_for_each_child_safe(child, next_child, src) { + n = rb_first(&src->rb_root_in); + while (n) { + child = container_of(n, struct callchain_node, rb_node_in); + n = rb_next(n); + rb_erase(&child->rb_node_in, &src->rb_root_in); + err = merge_chain_branch(cursor, dst, child); if (err) break; - list_del(&child->siblings); free(child); } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 2b585bc308cf..7bb36022377f 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -21,11 +21,11 @@ enum chain_order { struct callchain_node { struct callchain_node *parent; - struct list_head siblings; - struct list_head children; struct list_head val; - struct rb_node rb_node; /* to sort nodes in an rbtree */ - struct rb_root rb_root; /* sorted tree of children */ + struct rb_node rb_node_in; /* to insert nodes in an rbtree */ + struct rb_node rb_node; /* to sort nodes in an output tree */ + struct rb_root rb_root_in; /* input tree of children */ + struct rb_root rb_root; /* sorted output tree of children */ unsigned int val_nr; u64 hit; u64 children_hit; @@ -86,13 +86,12 @@ extern __thread struct callchain_cursor callchain_cursor; static inline void callchain_init(struct callchain_root *root) { - INIT_LIST_HEAD(&root->node.siblings); - INIT_LIST_HEAD(&root->node.children); INIT_LIST_HEAD(&root->node.val); root->node.parent = NULL; root->node.hit = 0; root->node.children_hit = 0; + root->node.rb_root_in = RB_ROOT; root->max_depth = 0; } -- cgit v1.2.2 From 09600e0f9ebb06235b852a646a3644b7d4a71aca Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 15 Oct 2013 11:01:56 +0900 Subject: perf tools: Compare dso's also when comparing symbols Linus reported that sometimes 'perf report -s symbol' exits without any message on TUI. David and Jiri found that it's because it failed to add a hist entry due to an invalid symbol length. It turns out that sorting by symbol (address) was broken since it only compares symbol addresses. The symbol address is a relative address within a dso thus just checking its address can result in merging unrelated symbols together. Fix it by checking dso before comparing symbol address. Reported-by: Linus Torvalds Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381802517-18812-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 32c56377e008..1f9821db9e77 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -182,9 +182,19 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) static int64_t sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) { + int64_t ret; + if (!left->ms.sym && !right->ms.sym) return right->level - left->level; + /* + * comparing symbol address alone is not enough since it's a + * relative address within a dso. + */ + ret = sort__dso_cmp(left, right); + if (ret != 0) + return ret; + return _sort__sym_cmp(left->ms.sym, right->ms.sym); } -- cgit v1.2.2 From f5fc14124c5cefdd052a2b2a6a3f0ed531540113 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 15 Oct 2013 16:27:32 +0200 Subject: perf tools: Add data object to handle perf data file This patch is adding 'struct perf_data_file' object as a placeholder for all attributes regarding perf.data file handling. Changing perf_session__new to take it as an argument. The rest of the functionality will be added later to keep this change simple enough, because all the places using perf_session are changed now. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381847254-28809-2-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data.h | 29 +++++++++++++++++++++++++++++ tools/perf/util/session.c | 12 ++++++------ tools/perf/util/session.h | 6 +++--- 3 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 tools/perf/util/data.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h new file mode 100644 index 000000000000..ffa0186e6000 --- /dev/null +++ b/tools/perf/util/data.h @@ -0,0 +1,29 @@ +#ifndef __PERF_DATA_H +#define __PERF_DATA_H + +#include + +enum perf_data_mode { + PERF_DATA_MODE_WRITE, + PERF_DATA_MODE_READ, +}; + +struct perf_data_file { + const char *path; + int fd; + bool is_pipe; + bool force; + enum perf_data_mode mode; +}; + +static inline bool perf_data_file__is_read(struct perf_data_file *file) +{ + return file->mode == PERF_DATA_MODE_READ; +} + +static inline bool perf_data_file__is_write(struct perf_data_file *file) +{ + return file->mode == PERF_DATA_MODE_WRITE; +} + +#endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d4559ca08ca6..e3f63df1d57c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -106,11 +106,11 @@ static void perf_session__destroy_kernel_maps(struct perf_session *self) machines__destroy_kernel_maps(&self->machines); } -struct perf_session *perf_session__new(const char *filename, int mode, - bool force, bool repipe, - struct perf_tool *tool) +struct perf_session *perf_session__new(struct perf_data_file *file, + bool repipe, struct perf_tool *tool) { struct perf_session *self; + const char *filename = file->path; struct stat st; size_t len; @@ -134,11 +134,11 @@ struct perf_session *perf_session__new(const char *filename, int mode, INIT_LIST_HEAD(&self->ordered_samples.to_free); machines__init(&self->machines); - if (mode == O_RDONLY) { - if (perf_session__open(self, force) < 0) + if (perf_data_file__is_read(file)) { + if (perf_session__open(self, file->force) < 0) goto out_delete; perf_session__set_id_hdr_size(self); - } else if (mode == O_WRONLY) { + } else if (perf_data_file__is_write(file)) { /* * In O_RDONLY mode this will be performed when reading the * kernel MMAP event, in perf_event__process_mmap(). diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 04bf7373a7e5..f2f6251fd62c 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -7,6 +7,7 @@ #include "machine.h" #include "symbol.h" #include "thread.h" +#include "data.h" #include #include @@ -49,9 +50,8 @@ struct perf_session { struct perf_tool; -struct perf_session *perf_session__new(const char *filename, int mode, - bool force, bool repipe, - struct perf_tool *tool); +struct perf_session *perf_session__new(struct perf_data_file *file, + bool repipe, struct perf_tool *tool); void perf_session__delete(struct perf_session *session); void perf_event_header__bswap(struct perf_event_header *self); -- cgit v1.2.2 From 6a4d98d787b38a130a67e78b64182b419899623a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 15 Oct 2013 16:27:33 +0200 Subject: perf tools: Add perf_data_file__open interface to data object Adding perf_data_file__open interface to data object to open the perf.data file for both read and write. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381847254-28809-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/data.h | 4 ++ tools/perf/util/session.c | 95 ++++++++++++------------------------ tools/perf/util/session.h | 2 +- 4 files changed, 155 insertions(+), 66 deletions(-) create mode 100644 tools/perf/util/data.c (limited to 'tools/perf/util') diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c new file mode 100644 index 000000000000..7d09faf85cf1 --- /dev/null +++ b/tools/perf/util/data.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include + +#include "data.h" +#include "util.h" + +static bool check_pipe(struct perf_data_file *file) +{ + struct stat st; + bool is_pipe = false; + int fd = perf_data_file__is_read(file) ? + STDIN_FILENO : STDOUT_FILENO; + + if (!file->path) { + if (!fstat(fd, &st) && S_ISFIFO(st.st_mode)) + is_pipe = true; + } else { + if (!strcmp(file->path, "-")) + is_pipe = true; + } + + if (is_pipe) + file->fd = fd; + + return file->is_pipe = is_pipe; +} + +static int check_backup(struct perf_data_file *file) +{ + struct stat st; + + if (!stat(file->path, &st) && st.st_size) { + /* TODO check errors properly */ + char oldname[PATH_MAX]; + snprintf(oldname, sizeof(oldname), "%s.old", + file->path); + unlink(oldname); + rename(file->path, oldname); + } + + return 0; +} + +static int open_file_read(struct perf_data_file *file) +{ + struct stat st; + int fd; + + fd = open(file->path, O_RDONLY); + if (fd < 0) { + int err = errno; + + pr_err("failed to open %s: %s", file->path, strerror(err)); + if (err == ENOENT && !strcmp(file->path, "perf.data")) + pr_err(" (try 'perf record' first)"); + pr_err("\n"); + return -err; + } + + if (fstat(fd, &st) < 0) + goto out_close; + + if (!file->force && st.st_uid && (st.st_uid != geteuid())) { + pr_err("file %s not owned by current user or root\n", + file->path); + goto out_close; + } + + if (!st.st_size) { + pr_info("zero-sized file (%s), nothing to do!\n", + file->path); + goto out_close; + } + + file->size = st.st_size; + return fd; + + out_close: + close(fd); + return -1; +} + +static int open_file_write(struct perf_data_file *file) +{ + if (check_backup(file)) + return -1; + + return open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); +} + +static int open_file(struct perf_data_file *file) +{ + int fd; + + fd = perf_data_file__is_read(file) ? + open_file_read(file) : open_file_write(file); + + file->fd = fd; + return fd < 0 ? -1 : 0; +} + +int perf_data_file__open(struct perf_data_file *file) +{ + if (check_pipe(file)) + return 0; + + if (!file->path) + file->path = "perf.data"; + + return open_file(file); +} + +void perf_data_file__close(struct perf_data_file *file) +{ + close(file->fd); +} diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index ffa0186e6000..d6c262e42f47 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -13,6 +13,7 @@ struct perf_data_file { int fd; bool is_pipe; bool force; + unsigned long size; enum perf_data_mode mode; }; @@ -26,4 +27,7 @@ static inline bool perf_data_file__is_write(struct perf_data_file *file) return file->mode == PERF_DATA_MODE_WRITE; } +int perf_data_file__open(struct perf_data_file *file); +void perf_data_file__close(struct perf_data_file *file); + #endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e3f63df1d57c..d857c18d2eeb 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -16,73 +16,35 @@ #include "perf_regs.h" #include "vdso.h" -static int perf_session__open(struct perf_session *self, bool force) +static int perf_session__open(struct perf_session *self) { - struct stat input_stat; - - if (!strcmp(self->filename, "-")) { - self->fd_pipe = true; - self->fd = STDIN_FILENO; - + if (self->fd_pipe) { if (perf_session__read_header(self) < 0) pr_err("incompatible file format (rerun with -v to learn more)"); - return 0; } - self->fd = open(self->filename, O_RDONLY); - if (self->fd < 0) { - int err = errno; - - pr_err("failed to open %s: %s", self->filename, strerror(err)); - if (err == ENOENT && !strcmp(self->filename, "perf.data")) - pr_err(" (try 'perf record' first)"); - pr_err("\n"); - return -errno; - } - - if (fstat(self->fd, &input_stat) < 0) - goto out_close; - - if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { - pr_err("file %s not owned by current user or root\n", - self->filename); - goto out_close; - } - - if (!input_stat.st_size) { - pr_info("zero-sized file (%s), nothing to do!\n", - self->filename); - goto out_close; - } - if (perf_session__read_header(self) < 0) { pr_err("incompatible file format (rerun with -v to learn more)"); - goto out_close; + return -1; } if (!perf_evlist__valid_sample_type(self->evlist)) { pr_err("non matching sample_type"); - goto out_close; + return -1; } if (!perf_evlist__valid_sample_id_all(self->evlist)) { pr_err("non matching sample_id_all"); - goto out_close; + return -1; } if (!perf_evlist__valid_read_format(self->evlist)) { pr_err("non matching read_format"); - goto out_close; + return -1; } - self->size = input_stat.st_size; return 0; - -out_close: - close(self->fd); - self->fd = -1; - return -1; } void perf_session__set_id_hdr_size(struct perf_session *session) @@ -110,35 +72,35 @@ struct perf_session *perf_session__new(struct perf_data_file *file, bool repipe, struct perf_tool *tool) { struct perf_session *self; - const char *filename = file->path; - struct stat st; - size_t len; - - if (!filename || !strlen(filename)) { - if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) - filename = "-"; - else - filename = "perf.data"; - } - len = strlen(filename); - self = zalloc(sizeof(*self) + len); - - if (self == NULL) + self = zalloc(sizeof(*self)); + if (!self) goto out; - memcpy(self->filename, filename, len); self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples); INIT_LIST_HEAD(&self->ordered_samples.sample_cache); INIT_LIST_HEAD(&self->ordered_samples.to_free); machines__init(&self->machines); - if (perf_data_file__is_read(file)) { - if (perf_session__open(self, file->force) < 0) + if (file) { + if (perf_data_file__open(file)) goto out_delete; - perf_session__set_id_hdr_size(self); - } else if (perf_data_file__is_write(file)) { + + self->fd = file->fd; + self->fd_pipe = file->is_pipe; + self->filename = file->path; + self->size = file->size; + + if (perf_data_file__is_read(file)) { + if (perf_session__open(self) < 0) + goto out_close; + + perf_session__set_id_hdr_size(self); + } + } + + if (!file || perf_data_file__is_write(file)) { /* * In O_RDONLY mode this will be performed when reading the * kernel MMAP event, in perf_event__process_mmap(). @@ -153,10 +115,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file, tool->ordered_samples = false; } -out: return self; -out_delete: + + out_close: + perf_data_file__close(file); + out_delete: perf_session__delete(self); + out: return NULL; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f2f6251fd62c..e1ca2d0ae541 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -39,7 +39,7 @@ struct perf_session { bool fd_pipe; bool repipe; struct ordered_samples ordered_samples; - char filename[1]; + const char *filename; }; #define PRINT_IP_OPT_IP (1<<0) -- cgit v1.2.2 From cc9784bd9fa9d8e27fdea61142398cb85ce401a8 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 15 Oct 2013 16:27:34 +0200 Subject: perf session: Separating data file properties from session Removing 'fd, fd_pipe, filename, size' from struct perf_session and replacing them with struct perf_data_file object. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Andi Kleen Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381847254-28809-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data.h | 15 +++++++++++++++ tools/perf/util/header.c | 22 +++++++++++++--------- tools/perf/util/session.c | 36 +++++++++++++++++++----------------- tools/perf/util/session.h | 5 +---- 4 files changed, 48 insertions(+), 30 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index d6c262e42f47..8c2df80152a5 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -27,6 +27,21 @@ static inline bool perf_data_file__is_write(struct perf_data_file *file) return file->mode == PERF_DATA_MODE_WRITE; } +static inline int perf_data_file__is_pipe(struct perf_data_file *file) +{ + return file->is_pipe; +} + +static inline int perf_data_file__fd(struct perf_data_file *file) +{ + return file->fd; +} + +static inline unsigned long perf_data_file__size(struct perf_data_file *file) +{ + return file->size; +} + int perf_data_file__open(struct perf_data_file *file); void perf_data_file__close(struct perf_data_file *file); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index c3e5a3b817ab..26d9520a0c1b 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -22,6 +22,7 @@ #include "vdso.h" #include "strbuf.h" #include "build-id.h" +#include "data.h" static bool no_buildid_cache = false; @@ -2189,7 +2190,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) { struct header_print_data hd; struct perf_header *header = &session->header; - int fd = session->fd; + int fd = perf_data_file__fd(session->file); hd.fp = fp; hd.full = full; @@ -2650,7 +2651,8 @@ static int perf_header__read_pipe(struct perf_session *session) struct perf_header *header = &session->header; struct perf_pipe_file_header f_header; - if (perf_file_header__read_pipe(&f_header, header, session->fd, + if (perf_file_header__read_pipe(&f_header, header, + perf_data_file__fd(session->file), session->repipe) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; @@ -2751,18 +2753,19 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, int perf_session__read_header(struct perf_session *session) { + struct perf_data_file *file = session->file; struct perf_header *header = &session->header; struct perf_file_header f_header; struct perf_file_attr f_attr; u64 f_id; int nr_attrs, nr_ids, i, j; - int fd = session->fd; + int fd = perf_data_file__fd(file); session->evlist = perf_evlist__new(); if (session->evlist == NULL) return -ENOMEM; - if (session->fd_pipe) + if (perf_data_file__is_pipe(file)) return perf_header__read_pipe(session); if (perf_file_header__read(&f_header, header, fd) < 0) @@ -2777,7 +2780,7 @@ int perf_session__read_header(struct perf_session *session) if (f_header.data.size == 0) { pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" "Was the 'perf record' command properly terminated?\n", - session->filename); + file->path); } nr_attrs = f_header.attrs.size / f_header.attr_size; @@ -2990,18 +2993,19 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, struct perf_session *session) { ssize_t size_read, padding, size = event->tracing_data.size; - off_t offset = lseek(session->fd, 0, SEEK_CUR); + int fd = perf_data_file__fd(session->file); + off_t offset = lseek(fd, 0, SEEK_CUR); char buf[BUFSIZ]; /* setup for reading amidst mmap */ - lseek(session->fd, offset + sizeof(struct tracing_data_event), + lseek(fd, offset + sizeof(struct tracing_data_event), SEEK_SET); - size_read = trace_report(session->fd, &session->pevent, + size_read = trace_report(fd, &session->pevent, session->repipe); padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; - if (readn(session->fd, buf, padding) < 0) { + if (readn(fd, buf, padding) < 0) { pr_err("%s: reading input file", __func__); return -1; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d857c18d2eeb..19fc71678c8e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -18,17 +18,16 @@ static int perf_session__open(struct perf_session *self) { - if (self->fd_pipe) { - if (perf_session__read_header(self) < 0) - pr_err("incompatible file format (rerun with -v to learn more)"); - return 0; - } + struct perf_data_file *file = self->file; if (perf_session__read_header(self) < 0) { pr_err("incompatible file format (rerun with -v to learn more)"); return -1; } + if (perf_data_file__is_pipe(file)) + return 0; + if (!perf_evlist__valid_sample_type(self->evlist)) { pr_err("non matching sample_type"); return -1; @@ -87,10 +86,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, if (perf_data_file__open(file)) goto out_delete; - self->fd = file->fd; - self->fd_pipe = file->is_pipe; - self->filename = file->path; - self->size = file->size; + self->file = file; if (perf_data_file__is_read(file)) { if (perf_session__open(self) < 0) @@ -158,7 +154,8 @@ void perf_session__delete(struct perf_session *self) perf_session__delete_threads(self); perf_session_env__delete(&self->header.env); machines__exit(&self->machines); - close(self->fd); + if (self->file) + perf_data_file__close(self->file); free(self); vdso__exit(); } @@ -1015,6 +1012,7 @@ static int perf_session_deliver_event(struct perf_session *session, static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, struct perf_tool *tool, u64 file_offset) { + int fd = perf_data_file__fd(session->file); int err; dump_event(session, event, file_offset, NULL); @@ -1028,7 +1026,7 @@ static int perf_session__process_user_event(struct perf_session *session, union return err; case PERF_RECORD_HEADER_TRACING_DATA: /* setup for reading amidst mmap */ - lseek(session->fd, file_offset, SEEK_SET); + lseek(fd, file_offset, SEEK_SET); return tool->tracing_data(tool, event, session); case PERF_RECORD_HEADER_BUILD_ID: return tool->build_id(tool, event, session); @@ -1154,6 +1152,7 @@ volatile int session_done; static int __perf_session__process_pipe_events(struct perf_session *self, struct perf_tool *tool) { + int fd = perf_data_file__fd(self->file); union perf_event *event; uint32_t size, cur_size = 0; void *buf = NULL; @@ -1172,7 +1171,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self, return -errno; more: event = buf; - err = readn(self->fd, event, sizeof(struct perf_event_header)); + err = readn(fd, event, sizeof(struct perf_event_header)); if (err <= 0) { if (err == 0) goto done; @@ -1204,7 +1203,7 @@ more: p += sizeof(struct perf_event_header); if (size - sizeof(struct perf_event_header)) { - err = readn(self->fd, p, size - sizeof(struct perf_event_header)); + err = readn(fd, p, size - sizeof(struct perf_event_header)); if (err <= 0) { if (err == 0) { pr_err("unexpected end of event stream\n"); @@ -1285,6 +1284,7 @@ int __perf_session__process_events(struct perf_session *session, u64 data_offset, u64 data_size, u64 file_size, struct perf_tool *tool) { + int fd = perf_data_file__fd(session->file); u64 head, page_offset, file_offset, file_pos, progress_next; int err, mmap_prot, mmap_flags, map_idx = 0; size_t mmap_size; @@ -1317,7 +1317,7 @@ int __perf_session__process_events(struct perf_session *session, mmap_flags = MAP_PRIVATE; } remap: - buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, + buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, fd, file_offset); if (buf == MAP_FAILED) { pr_err("failed to mmap file\n"); @@ -1382,16 +1382,17 @@ out_err: int perf_session__process_events(struct perf_session *self, struct perf_tool *tool) { + u64 size = perf_data_file__size(self->file); int err; if (perf_session__register_idle_thread(self) == NULL) return -ENOMEM; - if (!self->fd_pipe) + if (!perf_data_file__is_pipe(self->file)) err = __perf_session__process_events(self, self->header.data_offset, self->header.data_size, - self->size, tool); + size, tool); else err = __perf_session__process_pipe_events(self, tool); @@ -1615,13 +1616,14 @@ int perf_session__cpu_bitmap(struct perf_session *session, void perf_session__fprintf_info(struct perf_session *session, FILE *fp, bool full) { + int fd = perf_data_file__fd(session->file); struct stat st; int ret; if (session == NULL || fp == NULL) return; - ret = fstat(session->fd, &st); + ret = fstat(fd, &st); if (ret == -1) return; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index e1ca2d0ae541..27c74d38b868 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -30,16 +30,13 @@ struct ordered_samples { struct perf_session { struct perf_header header; - unsigned long size; struct machines machines; struct perf_evlist *evlist; struct pevent *pevent; struct events_stats stats; - int fd; - bool fd_pipe; bool repipe; struct ordered_samples ordered_samples; - const char *filename; + struct perf_data_file *file; }; #define PRINT_IP_OPT_IP (1<<0) -- cgit v1.2.2 From 91e95617429cb272fd908b1928a1915b37b9655f Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Fri, 18 Oct 2013 10:38:48 -0400 Subject: perf report: Add --max-stack option to limit callchain stack scan When callgraph data was included in the perf data file, it may take a long time to scan all those data and merge them together especially if the stored callchains are long and the perf data file itself is large, like a Gbyte or so. The callchain stack is currently limited to PERF_MAX_STACK_DEPTH (127). This is a large value. Usually the callgraph data that developers are most interested in are the first few levels, the rests are usually not looked at. This patch adds a new --max-stack option to perf-report to limit the depth of callchain stack data to look at to reduce the time it takes for perf-report to finish its processing. It trades the presence of trailing stack information with faster speed. The following table shows the elapsed time of doing perf-report on a perf.data file of size 985,531,828 bytes. --max_stack Elapsed Time Output data size ----------- ------------ ---------------- not set 88.0s 124,422,651 64 87.5s 116,303,213 32 87.2s 112,023,804 16 86.6s 94,326,380 8 59.9s 33,697,248 4 40.7s 10,116,637 -g none 27.1s 2,555,810 Signed-off-by: Waiman Long Acked-by: David Ahern Cc: Adrian Hunter Cc: Aswin Chandramouleeswaran Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Scott J Norton Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382107129-2010-4-git-send-email-Waiman.Long@hp.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 14 +++++++++----- tools/perf/util/machine.h | 3 ++- tools/perf/util/session.c | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6b861aefd99a..ea93425cce95 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1253,10 +1253,12 @@ static int machine__resolve_callchain_sample(struct machine *machine, struct thread *thread, struct ip_callchain *chain, struct symbol **parent, - struct addr_location *root_al) + struct addr_location *root_al, + int max_stack) { u8 cpumode = PERF_RECORD_MISC_USER; - unsigned int i; + int chain_nr = min(max_stack, (int)chain->nr); + int i; int err; callchain_cursor_reset(&callchain_cursor); @@ -1266,7 +1268,7 @@ static int machine__resolve_callchain_sample(struct machine *machine, return 0; } - for (i = 0; i < chain->nr; i++) { + for (i = 0; i < chain_nr; i++) { u64 ip; struct addr_location al; @@ -1338,12 +1340,14 @@ int machine__resolve_callchain(struct machine *machine, struct thread *thread, struct perf_sample *sample, struct symbol **parent, - struct addr_location *root_al) + struct addr_location *root_al, + int max_stack) { int ret; ret = machine__resolve_callchain_sample(machine, thread, - sample->callchain, parent, root_al); + sample->callchain, parent, + root_al, max_stack); if (ret) return ret; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index d44c09bdc45e..4c1f5d567f54 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -92,7 +92,8 @@ int machine__resolve_callchain(struct machine *machine, struct thread *thread, struct perf_sample *sample, struct symbol **parent, - struct addr_location *root_al); + struct addr_location *root_al, + int max_stack); /* * Default guest kernel is defined by parameter --guestkallsyms diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 19fc71678c8e..854c5aa4db0d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1512,7 +1512,8 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, if (symbol_conf.use_callchain && sample->callchain) { if (machine__resolve_callchain(machine, evsel, al.thread, - sample, NULL, NULL) != 0) { + sample, NULL, NULL, + PERF_MAX_STACK_DEPTH) != 0) { if (verbose) error("Failed to resolve callchain. Skipping\n"); return; -- cgit v1.2.2 From 5dbb6e81d85e55ee2b4cf523c1738e16f63e5400 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Fri, 18 Oct 2013 10:38:49 -0400 Subject: perf top: Add --max-stack option to limit callchain stack scan When the callgraph function is enabled (-G), it may take a long time to scan all the stack data and merge them accordingly. This patch adds a new --max-stack option to perf-top to limit the depth of callchain stack data to look at to reduce the time it takes for perf-top to finish its processing. It reduces the amount of information provided to the user in exchange for faster speed. Signed-off-by: Waiman Long Acked-by: David Ahern Tested-by: Davidlohr Bueso Cc: Adrian Hunter Cc: Aswin Chandramouleeswaran Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Scott J Norton Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382107129-2010-5-git-send-email-Waiman.Long@hp.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/top.h | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index b554ffc462b6..88cfeaff600b 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -24,6 +24,7 @@ struct perf_top { u64 exact_samples; u64 guest_us_samples, guest_kernel_samples; int print_entries, count_filter, delay_secs; + int max_stack; bool hide_kernel_symbols, hide_user_symbols, zero; bool use_tui, use_stdio; bool kptr_restrict_warned; -- cgit v1.2.2 From c824c4338ac47979c69ba6f8faab33670ae179df Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 22 Oct 2013 19:01:31 -0300 Subject: perf tools: Stop using 'self' in some more places As suggested by tglx, 'self' should be replaced by something that is more useful. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-fmblhc6tbb99tk1q8vowtsbj@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 6 +-- tools/perf/util/hist.c | 18 +++---- tools/perf/util/sort.c | 124 ++++++++++++++++++++++---------------------- tools/perf/util/strfilter.c | 46 ++++++++-------- tools/perf/util/thread.c | 72 ++++++++++++------------- 5 files changed, 133 insertions(+), 133 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 7ded71d19d75..a92770c98cc7 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -89,14 +89,14 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf) return raw - build_id; } -char *dso__build_id_filename(struct dso *self, char *bf, size_t size) +char *dso__build_id_filename(struct dso *dso, char *bf, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; - if (!self->has_build_id) + if (!dso->has_build_id) return NULL; - build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); + build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); if (bf == NULL) { if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir, build_id_hex, build_id_hex + 2) < 0) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index cca03831f41a..f0b863ef4896 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -406,7 +406,7 @@ out: return he; } -struct hist_entry *__hists__add_mem_entry(struct hists *self, +struct hist_entry *__hists__add_mem_entry(struct hists *hists, struct addr_location *al, struct symbol *sym_parent, struct mem_info *mi, @@ -429,14 +429,14 @@ struct hist_entry *__hists__add_mem_entry(struct hists *self, .level = al->level, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), - .hists = self, + .hists = hists, .mem_info = mi, .branch_info = NULL, }; - return add_hist_entry(self, &entry, al, period, weight); + return add_hist_entry(hists, &entry, al, period, weight); } -struct hist_entry *__hists__add_branch_entry(struct hists *self, +struct hist_entry *__hists__add_branch_entry(struct hists *hists, struct addr_location *al, struct symbol *sym_parent, struct branch_info *bi, @@ -460,14 +460,14 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), .branch_info = bi, - .hists = self, + .hists = hists, .mem_info = NULL, }; - return add_hist_entry(self, &entry, al, period, weight); + return add_hist_entry(hists, &entry, al, period, weight); } -struct hist_entry *__hists__add_entry(struct hists *self, +struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *sym_parent, u64 period, u64 weight, u64 transaction) @@ -488,13 +488,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), - .hists = self, + .hists = hists, .branch_info = NULL, .mem_info = NULL, .transaction = transaction, }; - return add_hist_entry(self, &entry, al, period, weight); + return add_hist_entry(hists, &entry, al, period, weight); } int64_t diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1f9821db9e77..19b4aa279d1e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -60,11 +60,11 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) return right->thread->tid - left->thread->tid; } -static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { return repsep_snprintf(bf, size, "%*s:%5d", width - 6, - self->thread->comm ?: "", self->thread->tid); + he->thread->comm ?: "", he->thread->tid); } struct sort_entry sort_thread = { @@ -94,10 +94,10 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) return strcmp(comm_l, comm_r); } -static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); + return repsep_snprintf(bf, size, "%*s", width, he->thread->comm); } struct sort_entry sort_comm = { @@ -148,10 +148,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf, return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); } -static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); + return _hist_entry__dso_snprintf(he->ms.map, bf, size, width); } struct sort_entry sort_dso = { @@ -234,11 +234,11 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, return ret; } -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, - self->level, bf, size, width); + return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip, + he->level, bf, size, width); } struct sort_entry sort_sym = { @@ -274,11 +274,11 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(left->srcline, right->srcline); } -static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width __maybe_unused) { - return repsep_snprintf(bf, size, "%s", self->srcline); + return repsep_snprintf(bf, size, "%s", he->srcline); } struct sort_entry sort_srcline = { @@ -302,11 +302,11 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(sym_l->name, sym_r->name); } -static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { return repsep_snprintf(bf, size, "%-*s", width, - self->parent ? self->parent->name : "[other]"); + he->parent ? he->parent->name : "[other]"); } struct sort_entry sort_parent = { @@ -324,10 +324,10 @@ 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) +static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, + size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%*d", width, self->cpu); + return repsep_snprintf(bf, size, "%*d", width, he->cpu); } struct sort_entry sort_cpu = { @@ -346,10 +346,10 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) right->branch_info->from.map); } -static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return _hist_entry__dso_snprintf(self->branch_info->from.map, + return _hist_entry__dso_snprintf(he->branch_info->from.map, bf, size, width); } @@ -360,10 +360,10 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) right->branch_info->to.map); } -static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return _hist_entry__dso_snprintf(self->branch_info->to.map, + return _hist_entry__dso_snprintf(he->branch_info->to.map, bf, size, width); } @@ -391,21 +391,21 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) return _sort__sym_cmp(to_l->sym, to_r->sym); } -static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - struct addr_map_symbol *from = &self->branch_info->from; + struct addr_map_symbol *from = &he->branch_info->from; return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, - self->level, bf, size, width); + he->level, bf, size, width); } -static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - struct addr_map_symbol *to = &self->branch_info->to; + struct addr_map_symbol *to = &he->branch_info->to; return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, - self->level, bf, size, width); + he->level, bf, size, width); } @@ -448,13 +448,13 @@ sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) return mp || p; } -static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width){ static const char *out = "N/A"; - if (self->branch_info->flags.predicted) + if (he->branch_info->flags.predicted) out = "N"; - else if (self->branch_info->flags.mispred) + else if (he->branch_info->flags.mispred) out = "Y"; return repsep_snprintf(bf, size, "%-*s", width, out); @@ -474,19 +474,19 @@ sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) return (int64_t)(r - l); } -static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { uint64_t addr = 0; struct map *map = NULL; struct symbol *sym = NULL; - if (self->mem_info) { - addr = self->mem_info->daddr.addr; - map = self->mem_info->daddr.map; - sym = self->mem_info->daddr.sym; + if (he->mem_info) { + addr = he->mem_info->daddr.addr; + map = he->mem_info->daddr.map; + sym = he->mem_info->daddr.sym; } - return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, + return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size, width); } @@ -504,13 +504,13 @@ sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) return _sort__dso_cmp(map_l, map_r); } -static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { struct map *map = NULL; - if (self->mem_info) - map = self->mem_info->daddr.map; + if (he->mem_info) + map = he->mem_info->daddr.map; return _hist_entry__dso_snprintf(map, bf, size, width); } @@ -534,14 +534,14 @@ sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); } -static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { const char *out; u64 mask = PERF_MEM_LOCK_NA; - if (self->mem_info) - mask = self->mem_info->data_src.mem_lock; + if (he->mem_info) + mask = he->mem_info->data_src.mem_lock; if (mask & PERF_MEM_LOCK_NA) out = "N/A"; @@ -583,7 +583,7 @@ static const char * const tlb_access[] = { }; #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) -static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { char out[64]; @@ -594,8 +594,8 @@ static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, out[0] = '\0'; - if (self->mem_info) - m = self->mem_info->data_src.mem_dtlb; + if (he->mem_info) + m = he->mem_info->data_src.mem_dtlb; hit = m & PERF_MEM_TLB_HIT; miss = m & PERF_MEM_TLB_MISS; @@ -660,7 +660,7 @@ static const char * const mem_lvl[] = { }; #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) -static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { char out[64]; @@ -669,8 +669,8 @@ static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, u64 m = PERF_MEM_LVL_NA; u64 hit, miss; - if (self->mem_info) - m = self->mem_info->data_src.mem_lvl; + if (he->mem_info) + m = he->mem_info->data_src.mem_lvl; out[0] = '\0'; @@ -728,7 +728,7 @@ static const char * const snoop_access[] = { }; #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) -static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { char out[64]; @@ -738,8 +738,8 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, out[0] = '\0'; - if (self->mem_info) - m = self->mem_info->data_src.mem_snoop; + if (he->mem_info) + m = he->mem_info->data_src.mem_snoop; for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { if (!(m & 0x1)) @@ -776,10 +776,10 @@ sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) return he_weight(left) - he_weight(right); } -static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); + return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he)); } struct sort_entry sort_local_weight = { @@ -795,10 +795,10 @@ sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) return left->stat.weight - right->stat.weight; } -static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); + return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight); } struct sort_entry sort_global_weight = { @@ -857,12 +857,12 @@ sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) right->branch_info->flags.abort; } -static int hist_entry__abort_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { static const char *out = "."; - if (self->branch_info->flags.abort) + if (he->branch_info->flags.abort) out = "A"; return repsep_snprintf(bf, size, "%-*s", width, out); } @@ -881,12 +881,12 @@ sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) right->branch_info->flags.in_tx; } -static int hist_entry__in_tx_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { static const char *out = "."; - if (self->branch_info->flags.in_tx) + if (he->branch_info->flags.in_tx) out = "T"; return repsep_snprintf(bf, size, "%-*s", width, out); @@ -940,10 +940,10 @@ int hist_entry__transaction_len(void) return len; } -static int hist_entry__transaction_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - u64 t = self->transaction; + u64 t = he->transaction; char buf[128]; char *p = buf; int i; @@ -1125,7 +1125,7 @@ int setup_sorting(void) return ret; } -static void sort_entry__setup_elide(struct sort_entry *self, +static void sort_entry__setup_elide(struct sort_entry *se, struct strlist *list, const char *list_name, FILE *fp) { @@ -1133,7 +1133,7 @@ static void sort_entry__setup_elide(struct sort_entry *self, if (fp != NULL) fprintf(fp, "# %s: %s\n", list_name, strlist__entry(list, 0)->s); - self->elide = true; + se->elide = true; } } diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 834c8ebfe38e..67e4a0082822 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -10,22 +10,22 @@ static const char *OP_not = "!"; /* Logical NOT */ #define is_operator(c) ((c) == '|' || (c) == '&' || (c) == '!') #define is_separator(c) (is_operator(c) || (c) == '(' || (c) == ')') -static void strfilter_node__delete(struct strfilter_node *self) +static void strfilter_node__delete(struct strfilter_node *node) { - if (self) { - if (self->p && !is_operator(*self->p)) - free((char *)self->p); - strfilter_node__delete(self->l); - strfilter_node__delete(self->r); - free(self); + if (node) { + if (node->p && !is_operator(*node->p)) + free((char *)node->p); + strfilter_node__delete(node->l); + strfilter_node__delete(node->r); + free(node); } } -void strfilter__delete(struct strfilter *self) +void strfilter__delete(struct strfilter *filter) { - if (self) { - strfilter_node__delete(self->root); - free(self); + if (filter) { + strfilter_node__delete(filter->root); + free(filter); } } @@ -170,30 +170,30 @@ struct strfilter *strfilter__new(const char *rules, const char **err) return ret; } -static bool strfilter_node__compare(struct strfilter_node *self, +static bool strfilter_node__compare(struct strfilter_node *node, const char *str) { - if (!self || !self->p) + if (!node || !node->p) return false; - switch (*self->p) { + switch (*node->p) { case '|': /* OR */ - return strfilter_node__compare(self->l, str) || - strfilter_node__compare(self->r, str); + return strfilter_node__compare(node->l, str) || + strfilter_node__compare(node->r, str); case '&': /* AND */ - return strfilter_node__compare(self->l, str) && - strfilter_node__compare(self->r, str); + return strfilter_node__compare(node->l, str) && + strfilter_node__compare(node->r, str); case '!': /* NOT */ - return !strfilter_node__compare(self->r, str); + return !strfilter_node__compare(node->r, str); default: - return strglobmatch(str, self->p); + return strglobmatch(str, node->p); } } /* Return true if STR matches the filter rules */ -bool strfilter__compare(struct strfilter *self, const char *str) +bool strfilter__compare(struct strfilter *node, const char *str) { - if (!self) + if (!node) return false; - return strfilter_node__compare(self->root, str); + return strfilter_node__compare(node->root, str); } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index e3d4a550a703..80d19a086072 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -9,51 +9,51 @@ struct thread *thread__new(pid_t pid, pid_t tid) { - struct thread *self = zalloc(sizeof(*self)); + struct thread *thread = zalloc(sizeof(*thread)); - if (self != NULL) { - map_groups__init(&self->mg); - self->pid_ = pid; - self->tid = tid; - self->ppid = -1; - self->comm = malloc(32); - if (self->comm) - snprintf(self->comm, 32, ":%d", self->tid); + if (thread != NULL) { + map_groups__init(&thread->mg); + thread->pid_ = pid; + thread->tid = tid; + thread->ppid = -1; + thread->comm = malloc(32); + if (thread->comm) + snprintf(thread->comm, 32, ":%d", thread->tid); } - return self; + return thread; } -void thread__delete(struct thread *self) +void thread__delete(struct thread *thread) { - map_groups__exit(&self->mg); - free(self->comm); - free(self); + map_groups__exit(&thread->mg); + free(thread->comm); + free(thread); } -int thread__set_comm(struct thread *self, const char *comm) +int thread__set_comm(struct thread *thread, const char *comm) { int err; - if (self->comm) - free(self->comm); - self->comm = strdup(comm); - err = self->comm == NULL ? -ENOMEM : 0; + if (thread->comm) + free(thread->comm); + thread->comm = strdup(comm); + err = thread->comm == NULL ? -ENOMEM : 0; if (!err) { - self->comm_set = true; + thread->comm_set = true; } return err; } -int thread__comm_len(struct thread *self) +int thread__comm_len(struct thread *thread) { - if (!self->comm_len) { - if (!self->comm) + if (!thread->comm_len) { + if (!thread->comm) return 0; - self->comm_len = strlen(self->comm); + thread->comm_len = strlen(thread->comm); } - return self->comm_len; + return thread->comm_len; } size_t thread__fprintf(struct thread *thread, FILE *fp) @@ -62,30 +62,30 @@ size_t thread__fprintf(struct thread *thread, FILE *fp) map_groups__fprintf(&thread->mg, verbose, fp); } -void thread__insert_map(struct thread *self, struct map *map) +void thread__insert_map(struct thread *thread, struct map *map) { - map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); - map_groups__insert(&self->mg, map); + map_groups__fixup_overlappings(&thread->mg, map, verbose, stderr); + map_groups__insert(&thread->mg, map); } -int thread__fork(struct thread *self, struct thread *parent) +int thread__fork(struct thread *thread, struct thread *parent) { int i; if (parent->comm_set) { - if (self->comm) - free(self->comm); - self->comm = strdup(parent->comm); - if (!self->comm) + if (thread->comm) + free(thread->comm); + thread->comm = strdup(parent->comm); + if (!thread->comm) return -ENOMEM; - self->comm_set = true; + thread->comm_set = true; } for (i = 0; i < MAP__NR_TYPES; ++i) - if (map_groups__clone(&self->mg, &parent->mg, i) < 0) + if (map_groups__clone(&thread->mg, &parent->mg, i) < 0) return -ENOMEM; - self->ppid = parent->tid; + thread->ppid = parent->tid; return 0; } -- cgit v1.2.2 From 7969ec7728ba6340de8000ddb0a8833273629d6a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 11 Oct 2013 16:10:23 +0900 Subject: perf probe: Support "$vars" meta argument syntax for local variables Support "$vars" meta argument syntax for tracing all local variables at probe point. Now you can trace all available local variables (including function parameters) at the probe point by passing $vars. # perf probe --add foo $vars This automatically finds all local variables at foo() and adds it as probe arguments. Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20131011071023.15557.51770.stgit@udc4-manage.rcp.hitachi.co.jp Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 1 - tools/perf/util/probe-finder.c | 96 ++++++++++++++++++++++++++++++++++++++---- tools/perf/util/probe-finder.h | 1 + 3 files changed, 89 insertions(+), 9 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 779b2dacd43f..9c6989ca2bea 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -47,7 +47,6 @@ #include "session.h" #define MAX_CMDLEN 256 -#define MAX_PROBE_ARGS 128 #define PERFPROBE_GROUP "probe" bool probe_event_dry_run; /* Dry run flag */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c09e0a9fdf4c..5a46be968c0b 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -1136,12 +1136,78 @@ found: return ret; } +struct local_vars_finder { + struct probe_finder *pf; + struct perf_probe_arg *args; + int max_args; + int nargs; + int ret; +}; + +/* Collect available variables in this scope */ +static int copy_variables_cb(Dwarf_Die *die_mem, void *data) +{ + struct local_vars_finder *vf = data; + int tag; + + tag = dwarf_tag(die_mem); + if (tag == DW_TAG_formal_parameter || + tag == DW_TAG_variable) { + if (convert_variable_location(die_mem, vf->pf->addr, + vf->pf->fb_ops, NULL) == 0) { + vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); + if (vf->args[vf->nargs].var == NULL) { + vf->ret = -ENOMEM; + return DIE_FIND_CB_END; + } + pr_debug(" %s", vf->args[vf->nargs].var); + vf->nargs++; + } + } + + if (dwarf_haspc(die_mem, vf->pf->addr)) + return DIE_FIND_CB_CONTINUE; + else + return DIE_FIND_CB_SIBLING; +} + +static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, + struct perf_probe_arg *args) +{ + Dwarf_Die die_mem; + int i; + int n = 0; + struct local_vars_finder vf = {.pf = pf, .args = args, + .max_args = MAX_PROBE_ARGS, .ret = 0}; + + for (i = 0; i < pf->pev->nargs; i++) { + /* var never be NULL */ + if (strcmp(pf->pev->args[i].var, "$vars") == 0) { + pr_debug("Expanding $vars into:"); + vf.nargs = n; + /* Special local variables */ + die_find_child(sc_die, copy_variables_cb, (void *)&vf, + &die_mem); + pr_debug(" (%d)\n", vf.nargs - n); + if (vf.ret < 0) + return vf.ret; + n = vf.nargs; + } else { + /* Copy normal argument */ + args[n] = pf->pev->args[i]; + n++; + } + } + return n; +} + /* Add a found probe point into trace event list */ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) { struct trace_event_finder *tf = container_of(pf, struct trace_event_finder, pf); struct probe_trace_event *tev; + struct perf_probe_arg *args; int ret, i; /* Check number of tevs */ @@ -1161,21 +1227,35 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); - /* Find each argument */ - tev->nargs = pf->pev->nargs; - tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); - if (tev->args == NULL) + /* Expand special probe argument if exist */ + args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); + if (args == NULL) return -ENOMEM; - for (i = 0; i < pf->pev->nargs; i++) { - pf->pvar = &pf->pev->args[i]; + + ret = expand_probe_args(sc_die, pf, args); + if (ret < 0) + goto end; + + tev->nargs = ret; + tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); + if (tev->args == NULL) { + ret = -ENOMEM; + goto end; + } + + /* Find each argument */ + for (i = 0; i < tev->nargs; i++) { + pf->pvar = &args[i]; pf->tvar = &tev->args[i]; /* Variable should be found from scope DIE */ ret = find_variable(sc_die, pf); if (ret != 0) - return ret; + break; } - return 0; +end: + free(args); + return ret; } /* Find probe_trace_events specified by perf_probe_event from debuginfo */ diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 3f0c29dd6ac5..d6dab0e0a937 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -7,6 +7,7 @@ #define MAX_PROBE_BUFFER 1024 #define MAX_PROBES 128 +#define MAX_PROBE_ARGS 128 static inline int is_c_varname(const char *name) { -- cgit v1.2.2 From 3d918a12a1b3088ac16ff37fa52760639d6e2403 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 11 Oct 2013 16:10:26 +0900 Subject: perf probe: Find fentry mcount fuzzed parameter location At this point, --fentry (mcount function entry) option for gcc fuzzes the debuginfo variable locations by skipping the mcount instruction offset (on x86, this is a 5 byte call instruction). This makes variable searching fail at the entry of functions which are mcount'ed. e.g.) Available variables at vfs_read @ (No matched variables) This patch adds additional location search at the function entry point to solve this issue, which tries to find the earliest address for the variable location. Note that this only works with function parameters (formal parameters) because any local variables should not exist on the function entry address (those are not initialized yet). With this patch, perf probe shows correct parameters if possible; # perf probe --vars vfs_read Available variables at vfs_read @ char* buf loff_t* pos size_t count struct file* file Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20131011071025.15557.13275.stgit@udc4-manage.rcp.hitachi.co.jp Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 5a46be968c0b..c04405296e5b 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -273,12 +273,15 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) /* * Convert a location into trace_arg. * If tvar == NULL, this just checks variable can be converted. + * If fentry == true and vr_die is a parameter, do huristic search + * for the location fuzzed by function entry mcount. */ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, - Dwarf_Op *fb_ops, + Dwarf_Op *fb_ops, Dwarf_Die *sp_die, struct probe_trace_arg *tvar) { Dwarf_Attribute attr; + Dwarf_Addr tmp = 0; Dwarf_Op *op; size_t nops; unsigned int regn; @@ -291,12 +294,29 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, goto static_var; /* TODO: handle more than 1 exprs */ - if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || - dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 || - nops == 0) { - /* TODO: Support const_value */ + if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) + return -EINVAL; /* Broken DIE ? */ + if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { + ret = dwarf_entrypc(sp_die, &tmp); + if (ret || addr != tmp || + dwarf_tag(vr_die) != DW_TAG_formal_parameter || + dwarf_highpc(sp_die, &tmp)) + return -ENOENT; + /* + * This is fuzzed by fentry mcount. We try to find the + * parameter location at the earliest address. + */ + for (addr += 1; addr <= tmp; addr++) { + if (dwarf_getlocation_addr(&attr, addr, &op, + &nops, 1) > 0) + goto found; + } return -ENOENT; } +found: + if (nops == 0) + /* TODO: Support const_value */ + return -ENOENT; if (op->atom == DW_OP_addr) { static_var: @@ -600,7 +620,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) dwarf_diename(vr_die)); ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, - pf->tvar); + &pf->sp_die, pf->tvar); if (ret == -ENOENT) pr_err("Failed to find the location of %s at this address.\n" " Perhaps, it has been optimized out.\n", pf->pvar->var); @@ -1148,13 +1168,15 @@ struct local_vars_finder { static int copy_variables_cb(Dwarf_Die *die_mem, void *data) { struct local_vars_finder *vf = data; + struct probe_finder *pf = vf->pf; int tag; tag = dwarf_tag(die_mem); if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { if (convert_variable_location(die_mem, vf->pf->addr, - vf->pf->fb_ops, NULL) == 0) { + vf->pf->fb_ops, &pf->sp_die, + NULL) == 0) { vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); if (vf->args[vf->nargs].var == NULL) { vf->ret = -ENOMEM; @@ -1302,7 +1324,8 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { ret = convert_variable_location(die_mem, af->pf.addr, - af->pf.fb_ops, NULL); + af->pf.fb_ops, &af->pf.sp_die, + NULL); if (ret == 0) { ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); pr_debug2("Add new var: %s\n", buf); -- cgit v1.2.2 From 56921becdd1eb0720603fc2e6e4c7f518196d917 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:17 +0300 Subject: perf tools: Do not accept parse_tag_value() overflow parse_tag_value() accepts an "unsigned long" and multiplies it according to a tag character. Do not accept the value if the multiplication overflows. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-14-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index c25e57b3acb2..28a0a89c1f73 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -386,6 +386,8 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) if (s != endptr) break; + if (value > ULONG_MAX / i->mult) + break; value *= i->mult; return value; } -- cgit v1.2.2 From 2fbe4abe944868aafdde233557ac85379b60ce46 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 Oct 2013 10:34:18 +0300 Subject: perf evlist: Validate that mmap_pages is not too big Amend perf_evlist__parse_mmap_pages() to check that the mmap_pages entered via the --mmap_pages/-m option is not too big. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1382427258-17495-15-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 85c4c80bcac8..2ce92eceb424 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -698,7 +698,8 @@ static size_t perf_evlist__mmap_size(unsigned long pages) int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, int unset __maybe_unused) { - unsigned int pages, val, *mmap_pages = opt->value; + unsigned int *mmap_pages = opt->value; + unsigned long pages, val; size_t size; static struct parse_tag tags[] = { { .tag = 'B', .mult = 1 }, @@ -709,12 +710,12 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, }; val = parse_tag_value(str, tags); - if (val != (unsigned int) -1) { + if (val != (unsigned long) -1) { /* we got file size value */ pages = PERF_ALIGN(val, page_size) / page_size; - if (!is_power_of_2(pages)) { + if (pages < (1UL << 31) && !is_power_of_2(pages)) { pages = next_pow2(pages); - pr_info("rounding mmap pages size to %u (%u pages)\n", + pr_info("rounding mmap pages size to %lu (%lu pages)\n", pages * page_size, pages); } } else { @@ -727,6 +728,11 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, } } + if (pages > UINT_MAX || pages > SIZE_MAX / page_size) { + pr_err("--mmap_pages/-m value too big\n"); + return -1; + } + size = perf_evlist__mmap_size(pages); if (!size) { pr_err("--mmap_pages/-m value must be a power of two."); -- cgit v1.2.2 From 4d3001fdfdfacd2b35ee74ff0f037274eeebd3f6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 23 Oct 2013 15:40:38 -0300 Subject: perf ui progress: Per progress bar state That will ease using a progress bar across multiple functions, like in the upcoming patches that will present a progress bar when collapsing histograms. Based on a previous patch by Namhyung Kim. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-cr7lq7ud9fj21bg7wvq27w1u@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 854c5aa4db0d..4ba7b548e055 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -503,13 +503,16 @@ static int flush_sample_queue(struct perf_session *s, struct perf_sample sample; u64 limit = os->next_flush; u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; - unsigned idx = 0, progress_next = os->nr_samples / 16; bool show_progress = limit == ULLONG_MAX; + struct ui_progress prog; int ret; if (!tool->ordered_samples || !limit) return 0; + if (show_progress) + ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); + list_for_each_entry_safe(iter, tmp, head, list) { if (session_done()) return 0; @@ -530,11 +533,9 @@ static int flush_sample_queue(struct perf_session *s, os->last_flush = iter->timestamp; list_del(&iter->list); list_add(&iter->list, &os->sample_cache); - if (show_progress && (++idx >= progress_next)) { - progress_next += os->nr_samples / 16; - ui_progress__update(idx, os->nr_samples, - "Processing time ordered events..."); - } + + if (show_progress) + ui_progress__update(&prog, 1); } if (list_empty(head)) { @@ -1285,12 +1286,13 @@ int __perf_session__process_events(struct perf_session *session, u64 file_size, struct perf_tool *tool) { int fd = perf_data_file__fd(session->file); - u64 head, page_offset, file_offset, file_pos, progress_next; + u64 head, page_offset, file_offset, file_pos; int err, mmap_prot, mmap_flags, map_idx = 0; size_t mmap_size; char *buf, *mmaps[NUM_MMAPS]; union perf_event *event; uint32_t size; + struct ui_progress prog; perf_tool__fill_defaults(tool); @@ -1301,7 +1303,7 @@ int __perf_session__process_events(struct perf_session *session, if (data_size && (data_offset + data_size < file_size)) file_size = data_offset + data_size; - progress_next = file_size / 16; + ui_progress__init(&prog, file_size, "Processing events..."); mmap_size = MMAP_SIZE; if (mmap_size > file_size) @@ -1356,11 +1358,7 @@ more: head += size; file_pos += size; - if (file_pos >= progress_next) { - progress_next += file_size / 16; - ui_progress__update(file_pos, file_size, - "Processing events..."); - } + ui_progress__update(&prog, size); if (session_done()) goto out; -- cgit v1.2.2 From c1fb5651bb40f9efaf32d280f39e06df7e352673 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 11 Oct 2013 14:15:38 +0900 Subject: perf tools: Show progress on histogram collapsing It can take quite amount of time so add progress bar UI to inform user. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Linus Torvalds Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381468543-25334-4-git-send-email-namhyung@kernel.org [ perf_progress -> ui_progress ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 5 ++++- tools/perf/util/hist.h | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f0b863ef4896..7e80253074b0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -399,6 +399,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, if (!he) return NULL; + hists->nr_entries++; rb_link_node(&he->rb_node_in, parent, p); rb_insert_color(&he->rb_node_in, hists->entries_in); out: @@ -604,7 +605,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) hists__filter_entry_by_symbol(hists, he); } -void hists__collapse_resort(struct hists *hists) +void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) { struct rb_root *root; struct rb_node *next; @@ -631,6 +632,8 @@ void hists__collapse_resort(struct hists *hists) */ hists__apply_filters(hists, n); } + if (prog) + ui_progress__update(prog, 1); } } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 20b175808cd3..0c7ce8bb8eba 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -5,6 +5,7 @@ #include #include "callchain.h" #include "header.h" +#include "ui/progress.h" extern struct callchain_param callchain_param; @@ -108,7 +109,7 @@ struct hist_entry *__hists__add_mem_entry(struct hists *self, u64 weight); void hists__output_resort(struct hists *self); -void hists__collapse_resort(struct hists *self); +void hists__collapse_resort(struct hists *self, struct ui_progress *prog); void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); void hists__output_recalc_col_len(struct hists *hists, int max_rows); -- cgit v1.2.2 From 53805eca3d89b095062c11a6798689bb0af09216 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 31 Oct 2013 16:47:45 -0700 Subject: perf tools: Remove cast of non-variadic function to variadic The 4fb71074a570 (perf ui/hist: Consolidate hpp helpers) cset introduced a cast of percent_color_snprintf to a function pointer type with varargs. Change percent_color_snprintf to be variadic and remove the cast. The symptom of this was all percentages being reported as 0.00% in perf report --stdio output on the armhf arch. Signed-off-by: Michael Hudson-Doyle Acked-by: Namhyung Kim Acked-by: Will Deacon Cc: Jean Pihet Cc: Jiri Olsa Cc: Will Deacon Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/87zjppvw7y.fsf@canonical.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/color.c | 11 +++++++++-- tools/perf/util/color.h | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 11e46da17bbb..66e44a5019d5 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -318,8 +318,15 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) return r; } -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) { - const char *color = get_percent_color(percent); + va_list args; + double percent; + const char *color; + + va_start(args, fmt); + percent = va_arg(args, double); + va_end(args); + color = get_percent_color(percent); return color_snprintf(bf, size, color, fmt, percent); } diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index dea082b79602..fced3840e99c 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -39,7 +39,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); -int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); +int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); -- cgit v1.2.2 From 9ef0438a957937bf0edc26d58bce891034ff9e30 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 24 Oct 2013 17:36:31 -0300 Subject: perf probe: Fix typo s/tyep/type/g. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-cznw5tnruonyoisxu8be11bv@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e41b0941e18f..2200dad4c3f4 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -583,7 +583,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, } if (die_find_member(&type, field->name, die_mem) == NULL) { - pr_warning("%s(tyep:%s) has no member %s.\n", varname, + pr_warning("%s(type:%s) has no member %s.\n", varname, dwarf_diename(&type), field->name); return -EINVAL; } -- cgit v1.2.2 From b9c5143a012a543c4ee872498d6dbae5c10beb2e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 11 Sep 2013 14:46:56 +0200 Subject: perf tools: Use an accessor to read thread comm As the thread comm is going to be implemented by way of a more complicated data structure than just a pointer to a string from the thread struct, convert the readers of comm to use an accessor instead of accessing it directly. The accessor will be later overriden to support an enhanced comm implementation. Signed-off-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Jiri Olsa Cc: David Ahern Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-wr683zwy94hmj4ibogmnv9ce@git.kernel.org [ Rename thread__comm_curr() to thread__comm_str() ] Signed-off-by: Namhyung Kim [ Fixed up some minor const pointer issues ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 4 ++-- tools/perf/util/scripting-engines/trace-event-perl.c | 2 +- tools/perf/util/scripting-engines/trace-event-python.c | 4 ++-- tools/perf/util/sort.c | 11 ++++++----- tools/perf/util/thread.c | 7 ++++++- tools/perf/util/thread.h | 1 + 6 files changed, 18 insertions(+), 11 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 49096ea58a15..7a2842ed53f3 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -721,10 +721,10 @@ int perf_event__preprocess_sample(const union perf_event *event, return -1; if (symbol_conf.comm_list && - !strlist__has_entry(symbol_conf.comm_list, thread->comm)) + !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) goto out_filtered; - dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); + dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); /* * Have we already created the kernel maps for this machine? * diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index c0c9795c4f02..d5e5969f6fea 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -273,7 +273,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, int cpu = sample->cpu; void *data = sample->raw_data; unsigned long long nsecs = sample->time; - char *comm = thread->comm; + const char *comm = thread__comm_str(thread); dSP; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 95d91a0b23af..53c20e7fd900 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -250,7 +250,7 @@ static void python_process_tracepoint(union perf_event *perf_event int cpu = sample->cpu; void *data = sample->raw_data; unsigned long long nsecs = sample->time; - char *comm = thread->comm; + const char *comm = thread__comm_str(thread); t = PyTuple_New(MAX_FIELDS); if (!t) @@ -389,7 +389,7 @@ static void python_process_general_event(union perf_event *perf_event pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize( (const char *)sample->raw_data, sample->raw_size)); pydict_set_item_string_decref(dict, "comm", - PyString_FromString(thread->comm)); + PyString_FromString(thread__comm_str(thread))); if (al->map) { pydict_set_item_string_decref(dict, "dso", PyString_FromString(al->map->dso->name)); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 19b4aa279d1e..835e8bdd869f 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -42,7 +42,7 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) return n; } -static int64_t cmp_null(void *l, void *r) +static int64_t cmp_null(const void *l, const void *r) { if (!l && !r) return 0; @@ -63,8 +63,9 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { + const char *comm = thread__comm_str(he->thread); return repsep_snprintf(bf, size, "%*s:%5d", width - 6, - he->thread->comm ?: "", he->thread->tid); + comm ?: "", he->thread->tid); } struct sort_entry sort_thread = { @@ -85,8 +86,8 @@ sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) static int64_t sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) { - char *comm_l = left->thread->comm; - char *comm_r = right->thread->comm; + const char *comm_l = thread__comm_str(left->thread); + const char *comm_r = thread__comm_str(right->thread); if (!comm_l || !comm_r) return cmp_null(comm_l, comm_r); @@ -97,7 +98,7 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%*s", width, he->thread->comm); + return repsep_snprintf(bf, size, "%*s", width, thread__comm_str(he->thread)); } struct sort_entry sort_comm = { diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 80d19a086072..56760079565b 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -45,6 +45,11 @@ int thread__set_comm(struct thread *thread, const char *comm) return err; } +const char *thread__comm_str(const struct thread *thread) +{ + return thread->comm; +} + int thread__comm_len(struct thread *thread) { if (!thread->comm_len) { @@ -58,7 +63,7 @@ int thread__comm_len(struct thread *thread) size_t thread__fprintf(struct thread *thread, FILE *fp) { - return fprintf(fp, "Thread %d %s\n", thread->tid, thread->comm) + + return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + map_groups__fprintf(&thread->mg, verbose, fp); } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 4ebbb40d46d4..6561ad21d9a7 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -35,6 +35,7 @@ static inline void thread__exited(struct thread *thread) int thread__set_comm(struct thread *self, const char *comm); int thread__comm_len(struct thread *self); +const char *thread__comm_str(const struct thread *thread); void thread__insert_map(struct thread *self, struct map *map); int thread__fork(struct thread *self, struct thread *parent); size_t thread__fprintf(struct thread *thread, FILE *fp); -- cgit v1.2.2 From 162f0befda3becc2cc9f44075fccc030e55baec1 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 11 Sep 2013 16:18:24 +0200 Subject: perf tools: Add time argument on COMM setting This way we can later delimit a lifecycle for the COMM and map a hist to a precise COMM:timeslice couple. PERF_RECORD_COMM and PERF_RECORD_FORK events that don't have PERF_SAMPLE_TIME samples can only send 0 value as a timestamp and thus should overwrite any previous COMM on a given thread because there is no sensible way to keep track of all the comms lifecycles in a thread without time informations. Signed-off-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Jiri Olsa Cc: David Ahern Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-6tyow99vgmmtt9qwr2u2lqd7@git.kernel.org [ Made it cope with PERF_RECORD_MMAP2 ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 28 ++++++++++++++-------------- tools/perf/util/machine.c | 39 ++++++++++++++++++++++----------------- tools/perf/util/machine.h | 21 ++++++++++++++------- tools/perf/util/session.c | 2 +- tools/perf/util/thread.c | 6 ++++-- tools/perf/util/thread.h | 4 ++-- 6 files changed, 57 insertions(+), 43 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 7a2842ed53f3..c26b3539187b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -512,18 +512,18 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) int perf_event__process_comm(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_comm_event(machine, event); + return machine__process_comm_event(machine, event, sample); } int perf_event__process_lost(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_lost_event(machine, event); + return machine__process_lost_event(machine, event, sample); } size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) @@ -546,18 +546,18 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_mmap_event(machine, event); + return machine__process_mmap_event(machine, event, sample); } int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_mmap2_event(machine, event); + return machine__process_mmap2_event(machine, event, sample); } size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) @@ -569,18 +569,18 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) int perf_event__process_fork(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_fork_event(machine, event); + return machine__process_fork_event(machine, event, sample); } int perf_event__process_exit(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_exit_event(machine, event); + return machine__process_exit_event(machine, event, sample); } size_t perf_event__fprintf(union perf_event *event, FILE *fp) @@ -611,10 +611,10 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) int perf_event__process(struct perf_tool *tool __maybe_unused, union perf_event *event, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct machine *machine) { - return machine__process_event(machine, event); + return machine__process_event(machine, event, sample); } void thread__find_addr_map(struct thread *self, diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ea93425cce95..ce034c183a7e 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -40,7 +40,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) return -ENOMEM; snprintf(comm, sizeof(comm), "[guest/%d]", pid); - thread__set_comm(thread, comm); + thread__set_comm(thread, comm, 0); } return 0; @@ -331,7 +331,8 @@ struct thread *machine__find_thread(struct machine *machine, pid_t tid) return __machine__findnew_thread(machine, 0, tid, false); } -int machine__process_comm_event(struct machine *machine, union perf_event *event) +int machine__process_comm_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample) { struct thread *thread = machine__findnew_thread(machine, event->comm.pid, @@ -340,7 +341,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event if (dump_trace) perf_event__fprintf_comm(event, stdout); - if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { + if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } @@ -349,7 +350,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event } int machine__process_lost_event(struct machine *machine __maybe_unused, - union perf_event *event) + union perf_event *event, struct perf_sample *sample __maybe_unused) { dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", event->lost.id, event->lost.lost); @@ -984,7 +985,8 @@ out_problem: } int machine__process_mmap2_event(struct machine *machine, - union perf_event *event) + union perf_event *event, + struct perf_sample *sample __maybe_unused) { u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; struct thread *thread; @@ -1031,7 +1033,8 @@ out_problem: return 0; } -int machine__process_mmap_event(struct machine *machine, union perf_event *event) +int machine__process_mmap_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample __maybe_unused) { u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; struct thread *thread; @@ -1088,7 +1091,8 @@ static void machine__remove_thread(struct machine *machine, struct thread *th) list_add_tail(&th->node, &machine->dead_threads); } -int machine__process_fork_event(struct machine *machine, union perf_event *event) +int machine__process_fork_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample) { struct thread *thread = machine__find_thread(machine, event->fork.tid); struct thread *parent = machine__findnew_thread(machine, @@ -1105,7 +1109,7 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event perf_event__fprintf_task(event, stdout); if (thread == NULL || parent == NULL || - thread__fork(thread, parent) < 0) { + thread__fork(thread, parent, sample->time) < 0) { dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); return -1; } @@ -1113,8 +1117,8 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event return 0; } -int machine__process_exit_event(struct machine *machine __maybe_unused, - union perf_event *event) +int machine__process_exit_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample __maybe_unused) { struct thread *thread = machine__find_thread(machine, event->fork.tid); @@ -1127,23 +1131,24 @@ int machine__process_exit_event(struct machine *machine __maybe_unused, return 0; } -int machine__process_event(struct machine *machine, union perf_event *event) +int machine__process_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample) { int ret; switch (event->header.type) { case PERF_RECORD_COMM: - ret = machine__process_comm_event(machine, event); break; + ret = machine__process_comm_event(machine, event, sample); break; case PERF_RECORD_MMAP: - ret = machine__process_mmap_event(machine, event); break; + ret = machine__process_mmap_event(machine, event, sample); break; case PERF_RECORD_MMAP2: - ret = machine__process_mmap2_event(machine, event); break; + ret = machine__process_mmap2_event(machine, event, sample); break; case PERF_RECORD_FORK: - ret = machine__process_fork_event(machine, event); break; + ret = machine__process_fork_event(machine, event, sample); break; case PERF_RECORD_EXIT: - ret = machine__process_exit_event(machine, event); break; + ret = machine__process_exit_event(machine, event, sample); break; case PERF_RECORD_LOST: - ret = machine__process_lost_event(machine, event); break; + ret = machine__process_lost_event(machine, event, sample); break; default: ret = -1; break; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 4c1f5d567f54..2389ba81fafe 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -40,13 +40,20 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type) struct thread *machine__find_thread(struct machine *machine, pid_t tid); -int machine__process_comm_event(struct machine *machine, union perf_event *event); -int machine__process_exit_event(struct machine *machine, union perf_event *event); -int machine__process_fork_event(struct machine *machine, union perf_event *event); -int machine__process_lost_event(struct machine *machine, union perf_event *event); -int machine__process_mmap_event(struct machine *machine, union perf_event *event); -int machine__process_mmap2_event(struct machine *machine, union perf_event *event); -int machine__process_event(struct machine *machine, union perf_event *event); +int machine__process_comm_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_exit_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_fork_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_lost_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_mmap_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_mmap2_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); +int machine__process_event(struct machine *machine, union perf_event *event, + struct perf_sample *sample); typedef void (*machine__process_t)(struct machine *machine, void *data); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4ba7b548e055..3c1b30103d54 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1100,7 +1100,7 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se { struct thread *thread = perf_session__findnew(self, 0); - if (thread == NULL || thread__set_comm(thread, "swapper")) { + if (thread == NULL || thread__set_comm(thread, "swapper", 0)) { pr_err("problem inserting idle task.\n"); thread = NULL; } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 56760079565b..0ea73fe383f5 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -31,7 +31,8 @@ void thread__delete(struct thread *thread) free(thread); } -int thread__set_comm(struct thread *thread, const char *comm) +int thread__set_comm(struct thread *thread, const char *comm, + u64 timestamp __maybe_unused) { int err; @@ -73,7 +74,8 @@ void thread__insert_map(struct thread *thread, struct map *map) map_groups__insert(&thread->mg, map); } -int thread__fork(struct thread *thread, struct thread *parent) +int thread__fork(struct thread *thread, struct thread *parent, + u64 timestamp __maybe_unused) { int i; diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 6561ad21d9a7..4e9724270a64 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -33,11 +33,11 @@ static inline void thread__exited(struct thread *thread) thread->dead = true; } -int thread__set_comm(struct thread *self, const char *comm); +int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); int thread__comm_len(struct thread *self); const char *thread__comm_str(const struct thread *thread); void thread__insert_map(struct thread *self, struct map *map); -int thread__fork(struct thread *self, struct thread *parent); +int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); size_t thread__fprintf(struct thread *thread, FILE *fp); static inline struct map *thread__find_map(struct thread *self, -- cgit v1.2.2 From 1902efe7f626fdebe1520f5ff11f1309ec506708 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 11 Sep 2013 16:56:44 +0200 Subject: perf tools: Add new COMM infrastructure This new COMM infrastructure provides two features: 1) It keeps track of all comms lifecycle for a given thread. This way we can associate a timeframe to any thread COMM, as long as PERF_SAMPLE_TIME samples are joined to COMM and fork events. As a result we should have more precise COMM sorted hists with seperated entries for pre and post exec time after a fork. 2) It also makes sure that a given COMM string is not duplicated but rather shared among the threads that refer to it. This way the threads COMM can be compared against pointer values from the sort infrastructure. Signed-off-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Jiri Olsa Cc: David Ahern Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-hwjf70b2wve9m2kosxiq8bb3@git.kernel.org [ Rename some accessor functions ] Signed-off-by: Namhyung Kim [ Use __ as separator for class__method for private comm_str methods ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/comm.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/comm.h | 20 +++++++++ tools/perf/util/thread.c | 92 +++++++++++++++++++++++++++++----------- tools/perf/util/thread.h | 3 +- 4 files changed, 196 insertions(+), 25 deletions(-) create mode 100644 tools/perf/util/comm.c create mode 100644 tools/perf/util/comm.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c new file mode 100644 index 000000000000..8b3ac9f0207f --- /dev/null +++ b/tools/perf/util/comm.c @@ -0,0 +1,106 @@ +#include "comm.h" +#include "util.h" +#include +#include + +struct comm_str { + char *str; + struct rb_node rb_node; + int ref; +}; + +/* Should perhaps be moved to struct machine */ +static struct rb_root comm_str_root; + +static void comm_str__get(struct comm_str *cs) +{ + cs->ref++; +} + +static void comm_str__put(struct comm_str *cs) +{ + if (!--cs->ref) { + rb_erase(&cs->rb_node, &comm_str_root); + free(cs->str); + free(cs); + } +} + +static struct comm_str *comm_str__alloc(const char *str) +{ + struct comm_str *cs; + + cs = zalloc(sizeof(*cs)); + if (!cs) + return NULL; + + cs->str = strdup(str); + if (!cs->str) { + free(cs); + return NULL; + } + + return cs; +} + +static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct comm_str *iter, *new; + int cmp; + + while (*p != NULL) { + parent = *p; + iter = rb_entry(parent, struct comm_str, rb_node); + + cmp = strcmp(str, iter->str); + if (!cmp) + return iter; + + if (cmp < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + new = comm_str__alloc(str); + if (!new) + return NULL; + + rb_link_node(&new->rb_node, parent, p); + rb_insert_color(&new->rb_node, root); + + return new; +} + +struct comm *comm__new(const char *str, u64 timestamp) +{ + struct comm *comm = zalloc(sizeof(*comm)); + + if (!comm) + return NULL; + + comm->start = timestamp; + + comm->comm_str = comm_str__findnew(str, &comm_str_root); + if (!comm->comm_str) { + free(comm); + return NULL; + } + + comm_str__get(comm->comm_str); + + return comm; +} + +void comm__free(struct comm *comm) +{ + comm_str__put(comm->comm_str); + free(comm); +} + +const char *comm__str(const struct comm *comm) +{ + return comm->comm_str->str; +} diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h new file mode 100644 index 000000000000..f62d215bede2 --- /dev/null +++ b/tools/perf/util/comm.h @@ -0,0 +1,20 @@ +#ifndef __PERF_COMM_H +#define __PERF_COMM_H + +#include "../perf.h" +#include +#include + +struct comm_str; + +struct comm { + struct comm_str *comm_str; + u64 start; + struct list_head list; +}; + +void comm__free(struct comm *comm); +struct comm *comm__new(const char *str, u64 timestamp); +const char *comm__str(const struct comm *comm); + +#endif /* __PERF_COMM_H */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 0ea73fe383f5..15c53c2e109e 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -6,9 +6,12 @@ #include "thread.h" #include "util.h" #include "debug.h" +#include "comm.h" struct thread *thread__new(pid_t pid, pid_t tid) { + char *comm_str; + struct comm *comm; struct thread *thread = zalloc(sizeof(*thread)); if (thread != NULL) { @@ -16,47 +19,88 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->pid_ = pid; thread->tid = tid; thread->ppid = -1; - thread->comm = malloc(32); - if (thread->comm) - snprintf(thread->comm, 32, ":%d", thread->tid); + INIT_LIST_HEAD(&thread->comm_list); + + comm_str = malloc(32); + if (!comm_str) + goto err_thread; + + snprintf(comm_str, 32, ":%d", tid); + comm = comm__new(comm_str, 0); + free(comm_str); + if (!comm) + goto err_thread; + + list_add(&comm->list, &thread->comm_list); } return thread; + +err_thread: + free(thread); + return NULL; } void thread__delete(struct thread *thread) { + struct comm *comm, *tmp; + map_groups__exit(&thread->mg); - free(thread->comm); + list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { + list_del(&comm->list); + comm__free(comm); + } + free(thread); } -int thread__set_comm(struct thread *thread, const char *comm, - u64 timestamp __maybe_unused) +static struct comm *thread__comm(const struct thread *thread) { - int err; + if (list_empty(&thread->comm_list)) + return NULL; - if (thread->comm) - free(thread->comm); - thread->comm = strdup(comm); - err = thread->comm == NULL ? -ENOMEM : 0; - if (!err) { - thread->comm_set = true; + return list_first_entry(&thread->comm_list, struct comm, list); +} + +/* CHECKME: time should always be 0 if event aren't ordered */ +int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) +{ + struct comm *new, *curr = thread__comm(thread); + + /* Override latest entry if it had no specific time coverage */ + if (!curr->start) { + list_del(&curr->list); + comm__free(curr); } - return err; + + new = comm__new(str, timestamp); + if (!new) + return -ENOMEM; + + list_add(&new->list, &thread->comm_list); + thread->comm_set = true; + + return 0; } const char *thread__comm_str(const struct thread *thread) { - return thread->comm; + const struct comm *comm = thread__comm(thread); + + if (!comm) + return NULL; + + return comm__str(comm); } +/* CHECKME: it should probably better return the max comm len from its comm list */ int thread__comm_len(struct thread *thread) { if (!thread->comm_len) { - if (!thread->comm) + const char *comm = thread__comm_str(thread); + if (!comm) return 0; - thread->comm_len = strlen(thread->comm); + thread->comm_len = strlen(comm); } return thread->comm_len; @@ -74,17 +118,17 @@ void thread__insert_map(struct thread *thread, struct map *map) map_groups__insert(&thread->mg, map); } -int thread__fork(struct thread *thread, struct thread *parent, - u64 timestamp __maybe_unused) +int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) { - int i; + int i, err; if (parent->comm_set) { - if (thread->comm) - free(thread->comm); - thread->comm = strdup(parent->comm); - if (!thread->comm) + const char *comm = thread__comm_str(parent); + if (!comm) return -ENOMEM; + err = thread__set_comm(thread, comm, timestamp); + if (!err) + return err; thread->comm_set = true; } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 4e9724270a64..8702c6b4163a 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -2,6 +2,7 @@ #define __PERF_THREAD_H #include +#include #include #include #include "symbol.h" @@ -18,7 +19,7 @@ struct thread { char shortname[3]; bool comm_set; bool dead; /* if set thread has exited */ - char *comm; + struct list_head comm_list; int comm_len; void *priv; -- cgit v1.2.2 From fedd63d3cdc9004df43b02df5c874b8957992fe8 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 11 Sep 2013 17:18:09 +0200 Subject: perf tools: Compare hists comm by addresses Now that comm strings are allocated only once and refcounted to be shared among threads, these can now be safely compared by addresses. This should remove most hists collapses on post processing. Signed-off-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Jiri Olsa Cc: David Ahern Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1381468543-25334-8-git-send-email-namhyung@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/util/sort.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 835e8bdd869f..bf91d0e5c16e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -80,7 +80,8 @@ struct sort_entry sort_thread = { static int64_t sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) { - return right->thread->tid - left->thread->tid; + /* Compare the addr that should be unique among comm */ + return thread__comm_str(right->thread) - thread__comm_str(left->thread); } static int64_t -- cgit v1.2.2 From 4dfced359fbc719a35527416f1b4b3999647f68b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 13 Sep 2013 16:28:57 +0900 Subject: perf tools: Get current comm instead of last one At insert time, a hist entry should reference comm at the time otherwise it'll get the last comm anyway. Signed-off-by: Namhyung Kim Acked-by: Frederic Weisbecker Tested-by: Jiri Olsa Cc: Frederic Weisbecker Link: http://lkml.kernel.org/n/tip-n6pykiiymtgmcjs834go2t8x@git.kernel.org [ Fixed up const pointer issues ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/comm.c | 15 +++++++++++++++ tools/perf/util/comm.h | 1 + tools/perf/util/hist.c | 3 +++ tools/perf/util/sort.c | 14 +++++--------- tools/perf/util/sort.h | 1 + tools/perf/util/thread.c | 6 +++--- tools/perf/util/thread.h | 2 ++ 7 files changed, 30 insertions(+), 12 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index 8b3ac9f0207f..ee0df0e24cdb 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c @@ -94,6 +94,21 @@ struct comm *comm__new(const char *str, u64 timestamp) return comm; } +void comm__override(struct comm *comm, const char *str, u64 timestamp) +{ + struct comm_str *old = comm->comm_str; + + comm->comm_str = comm_str__findnew(str, &comm_str_root); + if (!comm->comm_str) { + comm->comm_str = old; + return; + } + + comm->start = timestamp; + comm_str__get(comm->comm_str); + comm_str__put(old); +} + void comm__free(struct comm *comm) { comm_str__put(comm->comm_str); diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h index f62d215bede2..7a86e5656710 100644 --- a/tools/perf/util/comm.h +++ b/tools/perf/util/comm.h @@ -16,5 +16,6 @@ struct comm { void comm__free(struct comm *comm); struct comm *comm__new(const char *str, u64 timestamp); const char *comm__str(const struct comm *comm); +void comm__override(struct comm *comm, const char *str, u64 timestamp); #endif /* __PERF_COMM_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 7e80253074b0..30793f98c8bb 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -416,6 +416,7 @@ struct hist_entry *__hists__add_mem_entry(struct hists *hists, { struct hist_entry entry = { .thread = al->thread, + .comm = thread__comm(al->thread), .ms = { .map = al->map, .sym = al->sym, @@ -446,6 +447,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *hists, { struct hist_entry entry = { .thread = al->thread, + .comm = thread__comm(al->thread), .ms = { .map = bi->to.map, .sym = bi->to.sym, @@ -475,6 +477,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, { struct hist_entry entry = { .thread = al->thread, + .comm = thread__comm(al->thread), .ms = { .map = al->map, .sym = al->sym, diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index bf91d0e5c16e..3c1b75c8b9a6 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,5 +1,6 @@ #include "sort.h" #include "hist.h" +#include "comm.h" #include "symbol.h" regex_t parent_regex; @@ -81,25 +82,20 @@ static int64_t sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) { /* Compare the addr that should be unique among comm */ - return thread__comm_str(right->thread) - thread__comm_str(left->thread); + return comm__str(right->comm) - comm__str(left->comm); } static int64_t sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) { - const char *comm_l = thread__comm_str(left->thread); - const char *comm_r = thread__comm_str(right->thread); - - if (!comm_l || !comm_r) - return cmp_null(comm_l, comm_r); - - return strcmp(comm_l, comm_r); + /* Compare the addr that should be unique among comm */ + return comm__str(right->comm) - comm__str(left->comm); } static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { - return repsep_snprintf(bf, size, "%*s", width, thread__comm_str(he->thread)); + return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm)); } struct sort_entry sort_comm = { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index bf4333694d3a..f4e16f359d64 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -84,6 +84,7 @@ struct hist_entry { struct he_stat stat; struct map_symbol ms; struct thread *thread; + struct comm *comm; u64 ip; u64 transaction; s32 cpu; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 15c53c2e109e..cd8e2f592719 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -54,7 +54,7 @@ void thread__delete(struct thread *thread) free(thread); } -static struct comm *thread__comm(const struct thread *thread) +struct comm *thread__comm(const struct thread *thread) { if (list_empty(&thread->comm_list)) return NULL; @@ -69,8 +69,8 @@ int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) /* Override latest entry if it had no specific time coverage */ if (!curr->start) { - list_del(&curr->list); - comm__free(curr); + comm__override(curr, str, timestamp); + return 0; } new = comm__new(str, timestamp); diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 8702c6b4163a..373c055989ed 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -26,6 +26,7 @@ struct thread { }; struct machine; +struct comm; struct thread *thread__new(pid_t pid, pid_t tid); void thread__delete(struct thread *self); @@ -36,6 +37,7 @@ static inline void thread__exited(struct thread *thread) int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); int thread__comm_len(struct thread *self); +struct comm *thread__comm(const struct thread *thread); const char *thread__comm_str(const struct thread *thread); void thread__insert_map(struct thread *self, struct map *map); int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); -- cgit v1.2.2 From f852fd621ca19f557f2e3d05900366be7c7afb83 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:29 +0200 Subject: perf evsel: Add a debug print if perf_event_open fails There is a debug print (at verbose level 2) for each call to perf_event_open. Add another debug print if the call fails, and print the error number. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 3a334f001997..f0e65dec66a5 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1051,6 +1051,8 @@ retry_open: group_fd, flags); if (FD(evsel, cpu, thread) < 0) { err = -errno; + pr_debug2("perf_event_open failed, error %d\n", + err); goto try_fallback; } set_rlimit = NO_CHANGE; -- cgit v1.2.2 From 026359658aecd348bc5c4a136a26f204b169103b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:33 +0200 Subject: perf evlist: Add a debug print if event buffer mmap fails Add a debug print if mmap of the perf event ring buffer fails. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-6-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 0582f67fbefc..1c173ccb0ef2 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -607,6 +607,8 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, MAP_SHARED, fd, 0); if (evlist->mmap[idx].base == MAP_FAILED) { + pr_debug2("failed to mmap perf event ring buffer, error %d\n", + errno); evlist->mmap[idx].base = NULL; return -1; } -- cgit v1.2.2 From 1e7ed5ec54e3998bda6ea625599a0644404cb421 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:35 +0200 Subject: perf evsel: Always use perf_evsel__set_sample_bit() Always use perf_evsel__set_sample_bit() rather than just setting the bit. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-8-git-send-email-adrian.hunter@intel.com [ Cope with 3090ffb "perf: Disable PERF_RECORD_MMAP2 support" ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f0e65dec66a5..47bbf03aa7ef 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -663,7 +663,7 @@ void perf_evsel__config(struct perf_evsel *evsel, } if (opts->sample_address) - attr->sample_type |= PERF_SAMPLE_DATA_SRC; + perf_evsel__set_sample_bit(evsel, DATA_SRC); if (opts->no_delay) { attr->watermark = 0; @@ -675,13 +675,13 @@ void perf_evsel__config(struct perf_evsel *evsel, } if (opts->sample_weight) - attr->sample_type |= PERF_SAMPLE_WEIGHT; + perf_evsel__set_sample_bit(evsel, WEIGHT); attr->mmap = track; attr->comm = track; if (opts->sample_transaction) - attr->sample_type |= PERF_SAMPLE_TRANSACTION; + perf_evsel__set_sample_bit(evsel, TRANSACTION); /* * XXX see the function comment above -- cgit v1.2.2 From 87b955247d71975460774435241be3aa05218a7b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:36 +0200 Subject: perf evsel: Add missing overflow check for TRANSACTION Add missing overflow check for PERF_SAMPLE_TRANSACTION in perf_evsel__parse_sample(). Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-9-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 47bbf03aa7ef..b121717ce42a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1481,6 +1481,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, data->transaction = 0; if (type & PERF_SAMPLE_TRANSACTION) { + OVERFLOW_CHECK_u64(array); data->transaction = *array; array++; } -- cgit v1.2.2 From 42d88910c717ba21089251d0ca559abfef0df22d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:38 +0200 Subject: perf evsel: Synthesize PERF_SAMPLE_TRANSACTION Add missing PERF_SAMPLE_TRANSACTION to perf_event__synthesize_sample() and perf_event__sample_event_size(). This makes the "sample parsing" test pass. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-11-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index b121717ce42a..5280820ed389 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1578,6 +1578,9 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, if (type & PERF_SAMPLE_DATA_SRC) result += sizeof(u64); + if (type & PERF_SAMPLE_TRANSACTION) + result += sizeof(u64); + return result; } @@ -1751,6 +1754,11 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, array++; } + if (type & PERF_SAMPLE_TRANSACTION) { + *array = sample->transaction; + array++; + } + return 0; } -- cgit v1.2.2 From ac6976255076d4bf761abfbecb19d46af5b88046 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 1 Nov 2013 16:33:11 +0900 Subject: perf tools: Show single option when failed to parse Current option parser outputs whole option help string when it failed to parse an option. However this is not good for user if the command has many option, she might feel hard which one is related easily. Fix it by just showing the help message of the given option only. Signed-off-by: Namhyung Kim Requested-by: Ingo Molnar Acked-by: Ingo Molnar Reviewed-by: Ingo Molnar Enthusiastically-Supported-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383291195-24386-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-options.c | 217 ++++++++++++++++++++++++---------------- tools/perf/util/parse-options.h | 4 +- 2 files changed, 132 insertions(+), 89 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 2bc9e70df7e2..1caf7b9a01ee 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -339,10 +339,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, if (arg[1] != '-') { ctx->opt = arg + 1; if (internal_help && *ctx->opt == 'h') - return parse_options_usage(usagestr, options); + return usage_with_options_internal(usagestr, options, 0); switch (parse_short_opt(ctx, options)) { case -1: - return parse_options_usage(usagestr, options); + return parse_options_usage(usagestr, options, arg + 1, 1); case -2: goto unknown; default: @@ -352,10 +352,11 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, check_typos(arg + 1, options); while (ctx->opt) { if (internal_help && *ctx->opt == 'h') - return parse_options_usage(usagestr, options); + return usage_with_options_internal(usagestr, options, 0); + arg = ctx->opt; switch (parse_short_opt(ctx, options)) { case -1: - return parse_options_usage(usagestr, options); + return parse_options_usage(usagestr, options, arg, 1); case -2: /* fake a short option thing to hide the fact that we may have * started to parse aggregated stuff @@ -383,12 +384,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, if (internal_help && !strcmp(arg + 2, "help-all")) return usage_with_options_internal(usagestr, options, 1); if (internal_help && !strcmp(arg + 2, "help")) - return parse_options_usage(usagestr, options); + return usage_with_options_internal(usagestr, options, 0); if (!strcmp(arg + 2, "list-opts")) return PARSE_OPT_LIST; switch (parse_long_opt(ctx, arg + 2, options)) { case -1: - return parse_options_usage(usagestr, options); + return parse_options_usage(usagestr, options, arg + 2, 0); case -2: goto unknown; default: @@ -445,6 +446,89 @@ int parse_options(int argc, const char **argv, const struct option *options, #define USAGE_OPTS_WIDTH 24 #define USAGE_GAP 2 +static void print_option_help(const struct option *opts, int full) +{ + size_t pos; + int pad; + + if (opts->type == OPTION_GROUP) { + fputc('\n', stderr); + if (*opts->help) + fprintf(stderr, "%s\n", opts->help); + return; + } + if (!full && (opts->flags & PARSE_OPT_HIDDEN)) + return; + + pos = fprintf(stderr, " "); + if (opts->short_name) + pos += fprintf(stderr, "-%c", opts->short_name); + else + pos += fprintf(stderr, " "); + + if (opts->long_name && opts->short_name) + pos += fprintf(stderr, ", "); + if (opts->long_name) + pos += fprintf(stderr, "--%s", opts->long_name); + + switch (opts->type) { + case OPTION_ARGUMENT: + break; + case OPTION_LONG: + case OPTION_U64: + case OPTION_INTEGER: + case OPTION_UINTEGER: + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=]"); + else + pos += fprintf(stderr, "[]"); + else + pos += fprintf(stderr, " "); + break; + case OPTION_CALLBACK: + if (opts->flags & PARSE_OPT_NOARG) + break; + /* FALLTHROUGH */ + case OPTION_STRING: + if (opts->argh) { + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=<%s>]", opts->argh); + else + pos += fprintf(stderr, "[<%s>]", opts->argh); + else + pos += fprintf(stderr, " <%s>", opts->argh); + } else { + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=...]"); + else + pos += fprintf(stderr, "[...]"); + else + pos += fprintf(stderr, " ..."); + } + break; + default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ + case OPTION_END: + case OPTION_GROUP: + case OPTION_BIT: + case OPTION_BOOLEAN: + case OPTION_INCR: + case OPTION_SET_UINT: + case OPTION_SET_PTR: + break; + } + + if (pos <= USAGE_OPTS_WIDTH) + pad = USAGE_OPTS_WIDTH - pos; + else { + fputc('\n', stderr); + pad = USAGE_OPTS_WIDTH; + } + fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); +} + int usage_with_options_internal(const char * const *usagestr, const struct option *opts, int full) { @@ -464,87 +548,9 @@ int usage_with_options_internal(const char * const *usagestr, if (opts->type != OPTION_GROUP) fputc('\n', stderr); - for (; opts->type != OPTION_END; opts++) { - size_t pos; - int pad; - - if (opts->type == OPTION_GROUP) { - fputc('\n', stderr); - if (*opts->help) - fprintf(stderr, "%s\n", opts->help); - continue; - } - if (!full && (opts->flags & PARSE_OPT_HIDDEN)) - continue; - - pos = fprintf(stderr, " "); - if (opts->short_name) - pos += fprintf(stderr, "-%c", opts->short_name); - else - pos += fprintf(stderr, " "); - - if (opts->long_name && opts->short_name) - pos += fprintf(stderr, ", "); - if (opts->long_name) - pos += fprintf(stderr, "--%s", opts->long_name); + for ( ; opts->type != OPTION_END; opts++) + print_option_help(opts, full); - switch (opts->type) { - case OPTION_ARGUMENT: - break; - case OPTION_LONG: - case OPTION_U64: - case OPTION_INTEGER: - case OPTION_UINTEGER: - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=]"); - else - pos += fprintf(stderr, "[]"); - else - pos += fprintf(stderr, " "); - break; - case OPTION_CALLBACK: - if (opts->flags & PARSE_OPT_NOARG) - break; - /* FALLTHROUGH */ - case OPTION_STRING: - if (opts->argh) { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=<%s>]", opts->argh); - else - pos += fprintf(stderr, "[<%s>]", opts->argh); - else - pos += fprintf(stderr, " <%s>", opts->argh); - } else { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=...]"); - else - pos += fprintf(stderr, "[...]"); - else - pos += fprintf(stderr, " ..."); - } - break; - default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ - case OPTION_END: - case OPTION_GROUP: - case OPTION_BIT: - case OPTION_BOOLEAN: - case OPTION_INCR: - case OPTION_SET_UINT: - case OPTION_SET_PTR: - break; - } - - if (pos <= USAGE_OPTS_WIDTH) - pad = USAGE_OPTS_WIDTH - pos; - else { - fputc('\n', stderr); - pad = USAGE_OPTS_WIDTH; - } - fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); - } fputc('\n', stderr); return PARSE_OPT_HELP; @@ -559,9 +565,44 @@ void usage_with_options(const char * const *usagestr, } int parse_options_usage(const char * const *usagestr, - const struct option *opts) + const struct option *opts, + const char *optstr, bool short_opt) { - return usage_with_options_internal(usagestr, opts, 0); + if (!usagestr) + return PARSE_OPT_HELP; + + fprintf(stderr, "\n usage: %s\n", *usagestr++); + while (*usagestr && **usagestr) + fprintf(stderr, " or: %s\n", *usagestr++); + while (*usagestr) { + fprintf(stderr, "%s%s\n", + **usagestr ? " " : "", + *usagestr); + usagestr++; + } + fputc('\n', stderr); + + for ( ; opts->type != OPTION_END; opts++) { + if (short_opt) { + if (opts->short_name == *optstr) + break; + continue; + } + + if (opts->long_name == NULL) + continue; + + if (!prefixcmp(optstr, opts->long_name)) + break; + if (!prefixcmp(optstr, "no-") && + !prefixcmp(optstr + 3, opts->long_name)) + break; + } + + if (opts->type != OPTION_END) + print_option_help(opts, 0); + + return PARSE_OPT_HELP; } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 7bb5999940ca..b0241e28eaf7 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -158,7 +158,9 @@ struct parse_opt_ctx_t { }; extern int parse_options_usage(const char * const *usagestr, - const struct option *opts); + const struct option *opts, + const char *optstr, + bool short_opt); extern void parse_options_start(struct parse_opt_ctx_t *ctx, int argc, const char **argv, int flags); -- cgit v1.2.2 From cc03c54296ccbeca5363dfe8f49af42d14960f28 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 1 Nov 2013 16:33:15 +0900 Subject: perf stat: Enhance option parse error message Print related option help messages only when it failed to process options. While at it, modify parse_options_usage() to skip usage part so that it can be used for showing multiple option help messages naturally like below: $ perf stat -Bx, ls -B option not supported with -x usage: perf stat [] [] -B, --big-num print large numbers with thousands' separators -x, --field-separator print counts with custom separator Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Reviewed-by: Ingo Molnar Enthusiastically-Supported-by: Ingo Molnar Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383291195-24386-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-options.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 1caf7b9a01ee..31f404a032a9 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -569,7 +569,7 @@ int parse_options_usage(const char * const *usagestr, const char *optstr, bool short_opt) { if (!usagestr) - return PARSE_OPT_HELP; + goto opt; fprintf(stderr, "\n usage: %s\n", *usagestr++); while (*usagestr && **usagestr) @@ -582,6 +582,7 @@ int parse_options_usage(const char * const *usagestr, } fputc('\n', stderr); +opt: for ( ; opts->type != OPTION_END; opts++) { if (short_opt) { if (opts->short_name == *optstr) -- cgit v1.2.2 From 41a4e6e2a0237e8ac895f43158ef7c91ab7af157 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 31 Oct 2013 15:56:03 +0900 Subject: perf hists: Consolidate __hists__add_*entry() The __hists__add_{branch,mem}_entry() does almost the same thing that __hists__add_entry() does. Consolidate them into one. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Rodrigo Campos Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383202576-28141-2-git-send-email-namhyung@kernel.org [ Fixup clash with new COMM infrastructure ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 75 +++++--------------------------------------------- tools/perf/util/hist.h | 18 ++---------- 2 files changed, 10 insertions(+), 83 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 30793f98c8bb..822903eaa201 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -407,73 +407,12 @@ out: return he; } -struct hist_entry *__hists__add_mem_entry(struct hists *hists, - struct addr_location *al, - struct symbol *sym_parent, - struct mem_info *mi, - u64 period, - u64 weight) -{ - struct hist_entry entry = { - .thread = al->thread, - .comm = thread__comm(al->thread), - .ms = { - .map = al->map, - .sym = al->sym, - }, - .stat = { - .period = period, - .weight = weight, - .nr_events = 1, - }, - .cpu = al->cpu, - .ip = al->addr, - .level = al->level, - .parent = sym_parent, - .filtered = symbol__parent_filter(sym_parent), - .hists = hists, - .mem_info = mi, - .branch_info = NULL, - }; - return add_hist_entry(hists, &entry, al, period, weight); -} - -struct hist_entry *__hists__add_branch_entry(struct hists *hists, - struct addr_location *al, - struct symbol *sym_parent, - struct branch_info *bi, - u64 period, - u64 weight) -{ - struct hist_entry entry = { - .thread = al->thread, - .comm = thread__comm(al->thread), - .ms = { - .map = bi->to.map, - .sym = bi->to.sym, - }, - .cpu = al->cpu, - .ip = bi->to.addr, - .level = al->level, - .stat = { - .period = period, - .nr_events = 1, - .weight = weight, - }, - .parent = sym_parent, - .filtered = symbol__parent_filter(sym_parent), - .branch_info = bi, - .hists = hists, - .mem_info = NULL, - }; - - return add_hist_entry(hists, &entry, al, period, weight); -} - struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, - struct symbol *sym_parent, u64 period, - u64 weight, u64 transaction) + struct symbol *sym_parent, + struct branch_info *bi, + struct mem_info *mi, + u64 period, u64 weight, u64 transaction) { struct hist_entry entry = { .thread = al->thread, @@ -486,15 +425,15 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .ip = al->addr, .level = al->level, .stat = { - .period = period, .nr_events = 1, + .period = period, .weight = weight, }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), .hists = hists, - .branch_info = NULL, - .mem_info = NULL, + .branch_info = bi, + .mem_info = mi, .transaction = transaction, }; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9d2d022cdb79..307f1c742563 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -86,7 +86,9 @@ struct hists { struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *parent, u64 period, + struct symbol *parent, + struct branch_info *bi, + struct mem_info *mi, u64 period, u64 weight, u64 transaction); int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); @@ -95,20 +97,6 @@ int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, struct hists *hists); void hist_entry__free(struct hist_entry *); -struct hist_entry *__hists__add_branch_entry(struct hists *self, - struct addr_location *al, - struct symbol *sym_parent, - struct branch_info *bi, - u64 period, - u64 weight); - -struct hist_entry *__hists__add_mem_entry(struct hists *self, - struct addr_location *al, - struct symbol *sym_parent, - struct mem_info *mi, - u64 period, - u64 weight); - void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self, struct ui_progress *prog); -- cgit v1.2.2 From 4299a549979783668d787959d61ba22b6b200877 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Nov 2013 15:14:45 +0100 Subject: perf tools: Factor sysfs code into generic fs object Moving sysfs code into generic fs object and preparing it to carry procfs support. This should be merged with tools/lib/lk/debugfs.c at some point in the future. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383660887-1734-2-git-send-email-jolsa@redhat.com [ Added fs__ namespace qualifier to some more functions ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 2 +- tools/perf/util/fs.c | 107 +++++++++++++++++++++++++++++++++++++ tools/perf/util/fs.h | 6 +++ tools/perf/util/pmu.c | 2 +- tools/perf/util/python-ext-sources | 2 +- tools/perf/util/sysfs.c | 60 --------------------- tools/perf/util/sysfs.h | 6 --- 7 files changed, 116 insertions(+), 69 deletions(-) create mode 100644 tools/perf/util/fs.c create mode 100644 tools/perf/util/fs.h delete mode 100644 tools/perf/util/sysfs.c delete mode 100644 tools/perf/util/sysfs.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index beb8cf9f9976..4af5a23b2423 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -1,5 +1,5 @@ #include "util.h" -#include "sysfs.h" +#include "fs.h" #include "../perf.h" #include "cpumap.h" #include diff --git a/tools/perf/util/fs.c b/tools/perf/util/fs.c new file mode 100644 index 000000000000..a2413e842a02 --- /dev/null +++ b/tools/perf/util/fs.c @@ -0,0 +1,107 @@ + +/* TODO merge/factor into tools/lib/lk/debugfs.c */ + +#include "util.h" +#include "util/fs.h" + +static const char * const sysfs__fs_known_mountpoints[] = { + "/sys", + 0, +}; + +struct fs { + const char *name; + const char * const *mounts; + char path[PATH_MAX + 1]; + bool found; + long magic; +}; + +enum { + FS__SYSFS = 0, +}; + +static struct fs fs__entries[] = { + [FS__SYSFS] = { + .name = "sysfs", + .mounts = sysfs__fs_known_mountpoints, + .magic = SYSFS_MAGIC, + }, +}; + +static bool fs__read_mounts(struct fs *fs) +{ + bool found = false; + char type[100]; + FILE *fp; + + fp = fopen("/proc/mounts", "r"); + if (fp == NULL) + return NULL; + + while (!found && + fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", + fs->path, type) == 2) { + + if (strcmp(type, fs->name) == 0) + found = true; + } + + fclose(fp); + return fs->found = found; +} + +static int fs__valid_mount(const char *fs, long magic) +{ + struct statfs st_fs; + + if (statfs(fs, &st_fs) < 0) + return -ENOENT; + else if (st_fs.f_type != magic) + return -ENOENT; + + return 0; +} + +static bool fs__check_mounts(struct fs *fs) +{ + const char * const *ptr; + + ptr = fs->mounts; + while (*ptr) { + if (fs__valid_mount(*ptr, fs->magic) == 0) { + fs->found = true; + strcpy(fs->path, *ptr); + return true; + } + ptr++; + } + + return false; +} + +static const char *fs__get_mountpoint(struct fs *fs) +{ + if (fs__check_mounts(fs)) + return fs->path; + + return fs__read_mounts(fs) ? fs->path : NULL; +} + +static const char *fs__find_mountpoint(int idx) +{ + struct fs *fs = &fs__entries[idx]; + + if (fs->found) + return (const char *)fs->path; + + return fs__get_mountpoint(fs); +} + +#define FIND_MOUNTPOINT(name, idx) \ +const char *name##_find_mountpoint(void) \ +{ \ + return fs__find_mountpoint(idx); \ +} + +FIND_MOUNTPOINT(sysfs, FS__SYSFS); diff --git a/tools/perf/util/fs.h b/tools/perf/util/fs.h new file mode 100644 index 000000000000..082edbd2159b --- /dev/null +++ b/tools/perf/util/fs.h @@ -0,0 +1,6 @@ +#ifndef __PERF_FS +#define __PERF_FS + +const char *sysfs_find_mountpoint(void); + +#endif /* __PERF_FS */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 64362fe45b71..45b42dffcd70 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -4,7 +4,7 @@ #include #include #include -#include "sysfs.h" +#include "fs.h" #include "util.h" #include "pmu.h" #include "parse-events.h" diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index f75ae1b9900c..239036fb2b2c 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -17,5 +17,5 @@ util/xyarray.c util/cgroup.c util/rblist.c util/strlist.c -util/sysfs.c +util/fs.c ../../lib/rbtree.c diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c deleted file mode 100644 index f71e9eafe15a..000000000000 --- a/tools/perf/util/sysfs.c +++ /dev/null @@ -1,60 +0,0 @@ - -#include "util.h" -#include "sysfs.h" - -static const char * const sysfs_known_mountpoints[] = { - "/sys", - 0, -}; - -static int sysfs_found; -char sysfs_mountpoint[PATH_MAX + 1]; - -static int sysfs_valid_mountpoint(const char *sysfs) -{ - struct statfs st_fs; - - if (statfs(sysfs, &st_fs) < 0) - return -ENOENT; - else if (st_fs.f_type != (long) SYSFS_MAGIC) - return -ENOENT; - - return 0; -} - -const char *sysfs_find_mountpoint(void) -{ - const char * const *ptr; - char type[100]; - FILE *fp; - - if (sysfs_found) - return (const char *) sysfs_mountpoint; - - ptr = sysfs_known_mountpoints; - while (*ptr) { - if (sysfs_valid_mountpoint(*ptr) == 0) { - sysfs_found = 1; - strcpy(sysfs_mountpoint, *ptr); - return sysfs_mountpoint; - } - ptr++; - } - - /* give up and parse /proc/mounts */ - fp = fopen("/proc/mounts", "r"); - if (fp == NULL) - return NULL; - - while (!sysfs_found && - fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", - sysfs_mountpoint, type) == 2) { - - if (strcmp(type, "sysfs") == 0) - sysfs_found = 1; - } - - fclose(fp); - - return sysfs_found ? sysfs_mountpoint : NULL; -} diff --git a/tools/perf/util/sysfs.h b/tools/perf/util/sysfs.h deleted file mode 100644 index a813b7203938..000000000000 --- a/tools/perf/util/sysfs.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __SYSFS_H__ -#define __SYSFS_H__ - -const char *sysfs_find_mountpoint(void); - -#endif /* __DEBUGFS_H__ */ -- cgit v1.2.2 From cf38fadade52df937521dd70d4437df1a9354cd9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 5 Nov 2013 14:48:50 -0300 Subject: perf fs: Rename NAME_find_mountpoint() to NAME__mountpoint() Shorten it, "finding" it is an implementation detail, what callers want is the pathname, not to ask for it to _always_ do the lookup. And the existing implementation already caches it, i.e. it doesn't "finds" it on every call. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-r24wa4bvtccg7mnkessrbbdj@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 4 ++-- tools/perf/util/fs.c | 12 ++++++------ tools/perf/util/fs.h | 2 +- tools/perf/util/pmu.c | 15 +++++---------- 4 files changed, 14 insertions(+), 19 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 4af5a23b2423..a9b48c42e81e 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -216,7 +216,7 @@ int cpu_map__get_socket(struct cpu_map *map, int idx) cpu = map->map[idx]; - mnt = sysfs_find_mountpoint(); + mnt = sysfs__mountpoint(); if (!mnt) return -1; @@ -279,7 +279,7 @@ int cpu_map__get_core(struct cpu_map *map, int idx) cpu = map->map[idx]; - mnt = sysfs_find_mountpoint(); + mnt = sysfs__mountpoint(); if (!mnt) return -1; diff --git a/tools/perf/util/fs.c b/tools/perf/util/fs.c index a2413e842a02..77bac4e4c115 100644 --- a/tools/perf/util/fs.c +++ b/tools/perf/util/fs.c @@ -88,7 +88,7 @@ static const char *fs__get_mountpoint(struct fs *fs) return fs__read_mounts(fs) ? fs->path : NULL; } -static const char *fs__find_mountpoint(int idx) +static const char *fs__mountpoint(int idx) { struct fs *fs = &fs__entries[idx]; @@ -98,10 +98,10 @@ static const char *fs__find_mountpoint(int idx) return fs__get_mountpoint(fs); } -#define FIND_MOUNTPOINT(name, idx) \ -const char *name##_find_mountpoint(void) \ -{ \ - return fs__find_mountpoint(idx); \ +#define FS__MOUNTPOINT(name, idx) \ +const char *name##__mountpoint(void) \ +{ \ + return fs__mountpoint(idx); \ } -FIND_MOUNTPOINT(sysfs, FS__SYSFS); +FS__MOUNTPOINT(sysfs, FS__SYSFS); diff --git a/tools/perf/util/fs.h b/tools/perf/util/fs.h index 082edbd2159b..a7561c83c33c 100644 --- a/tools/perf/util/fs.h +++ b/tools/perf/util/fs.h @@ -1,6 +1,6 @@ #ifndef __PERF_FS #define __PERF_FS -const char *sysfs_find_mountpoint(void); +const char *sysfs__mountpoint(void); #endif /* __PERF_FS */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 45b42dffcd70..c232d8dd410b 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -77,9 +77,8 @@ static int pmu_format(const char *name, struct list_head *format) { struct stat st; char path[PATH_MAX]; - const char *sysfs; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return -1; @@ -166,9 +165,8 @@ static int pmu_aliases(const char *name, struct list_head *head) { struct stat st; char path[PATH_MAX]; - const char *sysfs; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return -1; @@ -212,11 +210,10 @@ static int pmu_type(const char *name, __u32 *type) { struct stat st; char path[PATH_MAX]; - const char *sysfs; FILE *file; int ret = 0; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return -1; @@ -241,11 +238,10 @@ static int pmu_type(const char *name, __u32 *type) static void pmu_read_sysfs(void) { char path[PATH_MAX]; - const char *sysfs; DIR *dir; struct dirent *dent; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return; @@ -270,11 +266,10 @@ static struct cpu_map *pmu_cpumask(const char *name) { struct stat st; char path[PATH_MAX]; - const char *sysfs; FILE *file; struct cpu_map *cpus; + const char *sysfs = sysfs__mountpoint(); - sysfs = sysfs_find_mountpoint(); if (!sysfs) return NULL; -- cgit v1.2.2 From a9862418547e818aa5842840aecfa81d733b97e9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Nov 2013 15:14:46 +0100 Subject: perf fs: Add procfs support Adding procfs support into fs class. The interface function: const char *procfs__mountpoint(void); provides existing mountpoint path for procfs. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383660887-1734-3-git-send-email-jolsa@redhat.com [ Fixup namespace ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/fs.c | 16 ++++++++++++++-- tools/perf/util/fs.h | 1 + tools/perf/util/include/linux/magic.h | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/fs.c b/tools/perf/util/fs.c index 77bac4e4c115..f5be1f26e724 100644 --- a/tools/perf/util/fs.c +++ b/tools/perf/util/fs.c @@ -9,6 +9,11 @@ static const char * const sysfs__fs_known_mountpoints[] = { 0, }; +static const char * const procfs__known_mountpoints[] = { + "/proc", + 0, +}; + struct fs { const char *name; const char * const *mounts; @@ -18,7 +23,8 @@ struct fs { }; enum { - FS__SYSFS = 0, + FS__SYSFS = 0, + FS__PROCFS = 1, }; static struct fs fs__entries[] = { @@ -27,6 +33,11 @@ static struct fs fs__entries[] = { .mounts = sysfs__fs_known_mountpoints, .magic = SYSFS_MAGIC, }, + [FS__PROCFS] = { + .name = "proc", + .mounts = procfs__known_mountpoints, + .magic = PROC_SUPER_MAGIC, + }, }; static bool fs__read_mounts(struct fs *fs) @@ -104,4 +115,5 @@ const char *name##__mountpoint(void) \ return fs__mountpoint(idx); \ } -FS__MOUNTPOINT(sysfs, FS__SYSFS); +FS__MOUNTPOINT(sysfs, FS__SYSFS); +FS__MOUNTPOINT(procfs, FS__PROCFS); diff --git a/tools/perf/util/fs.h b/tools/perf/util/fs.h index a7561c83c33c..5e09ce1bab0e 100644 --- a/tools/perf/util/fs.h +++ b/tools/perf/util/fs.h @@ -2,5 +2,6 @@ #define __PERF_FS const char *sysfs__mountpoint(void); +const char *procfs__mountpoint(void); #endif /* __PERF_FS */ diff --git a/tools/perf/util/include/linux/magic.h b/tools/perf/util/include/linux/magic.h index 58b64ed4da12..07d63cf3e0f6 100644 --- a/tools/perf/util/include/linux/magic.h +++ b/tools/perf/util/include/linux/magic.h @@ -9,4 +9,8 @@ #define SYSFS_MAGIC 0x62656572 #endif +#ifndef PROC_SUPER_MAGIC +#define PROC_SUPER_MAGIC 0x9fa0 +#endif + #endif -- cgit v1.2.2 From 714647bdc516330e4405b39677d7f763e016c685 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Nov 2013 15:14:47 +0100 Subject: perf tools: Check maximum frequency rate for record/top Adding the check for maximum allowed frequency rate defined in following file: /proc/sys/kernel/perf_event_max_sample_rate When we cross the maximum value we fail and display detailed error message with advise. $ perf record -F 3000 ls Maximum frequency rate (2000) reached. Please use -F freq option with lower value or consider tweaking /proc/sys/kernel/perf_event_max_sample_rate. In case user does not specify the frequency and the default value cross the maximum, we display warning and set the frequency value to the current maximum. $ perf record ls Lowering default frequency rate to 2000. Please consider tweaking /proc/sys/kernel/perf_event_max_sample_rate. Same messages are used for 'perf top'. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383660887-1734-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.h | 1 + tools/perf/util/record.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 6e8acc9abe38..0617ce2a0679 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -99,6 +99,7 @@ void perf_evlist__set_id_pos(struct perf_evlist *evlist); bool perf_can_sample_identifier(void); void perf_evlist__config(struct perf_evlist *evlist, struct perf_record_opts *opts); +int perf_record_opts__config(struct perf_record_opts *opts); int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct perf_target *target, diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 18d73aa2f0f8..c8845b107f60 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -2,6 +2,8 @@ #include "evsel.h" #include "cpumap.h" #include "parse-events.h" +#include "fs.h" +#include "util.h" typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); @@ -106,3 +108,72 @@ void perf_evlist__config(struct perf_evlist *evlist, perf_evlist__set_id_pos(evlist); } + +static int get_max_rate(unsigned int *rate) +{ + char path[PATH_MAX]; + const char *procfs = procfs__mountpoint(); + + if (!procfs) + return -1; + + snprintf(path, PATH_MAX, + "%s/sys/kernel/perf_event_max_sample_rate", procfs); + + return filename__read_int(path, (int *) rate); +} + +static int perf_record_opts__config_freq(struct perf_record_opts *opts) +{ + bool user_freq = opts->user_freq != UINT_MAX; + unsigned int max_rate; + + if (opts->user_interval != ULLONG_MAX) + opts->default_interval = opts->user_interval; + if (user_freq) + opts->freq = opts->user_freq; + + /* + * User specified count overrides default frequency. + */ + if (opts->default_interval) + opts->freq = 0; + else if (opts->freq) { + opts->default_interval = opts->freq; + } else { + pr_err("frequency and count are zero, aborting\n"); + return -1; + } + + if (get_max_rate(&max_rate)) + return 0; + + /* + * User specified frequency is over current maximum. + */ + if (user_freq && (max_rate < opts->freq)) { + pr_err("Maximum frequency rate (%u) reached.\n" + "Please use -F freq option with lower value or consider\n" + "tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n", + max_rate); + return -1; + } + + /* + * Default frequency is over current maximum. + */ + if (max_rate < opts->freq) { + pr_warning("Lowering default frequency rate to %u.\n" + "Please consider tweaking " + "/proc/sys/kernel/perf_event_max_sample_rate.\n", + max_rate); + opts->freq = max_rate; + } + + return 0; +} + +int perf_record_opts__config(struct perf_record_opts *opts) +{ + return perf_record_opts__config_freq(opts); +} -- cgit v1.2.2 From 316c7136f8bad924609163b9b115f68d59a68c82 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 5 Nov 2013 15:32:36 -0300 Subject: perf tools: Finish the removal of 'self' arguments They convey no information, perhaps I was bitten by some snake at some point, complete the detox by naming the last of those arguments more sensibly. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-u1r0dnjoro08dgztiy2g3t2q@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.h | 3 +- tools/perf/util/event.c | 6 +- tools/perf/util/event.h | 3 +- tools/perf/util/evlist.h | 2 +- tools/perf/util/hist.h | 33 +++++------ tools/perf/util/probe-finder.c | 113 +++++++++++++++++++------------------- tools/perf/util/probe-finder.h | 10 ++-- tools/perf/util/pstack.h | 10 ++-- tools/perf/util/session.c | 121 ++++++++++++++++++++--------------------- tools/perf/util/session.h | 27 ++++----- tools/perf/util/sort.h | 2 +- tools/perf/util/strfilter.c | 32 +++++------ tools/perf/util/strfilter.h | 12 ++-- tools/perf/util/thread.h | 10 ++-- 14 files changed, 191 insertions(+), 193 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a811f5c62e18..929f28a7c14d 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -10,10 +10,9 @@ extern struct perf_tool build_id__mark_dso_hit_ops; struct dso; int build_id__sprintf(const u8 *build_id, int len, char *bf); -char *dso__build_id_filename(struct dso *self, char *bf, size_t size); +char *dso__build_id_filename(struct dso *dso, char *bf, size_t size); int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine); - #endif diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index c26b3539187b..ec9ae1114ed4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -617,15 +617,15 @@ int perf_event__process(struct perf_tool *tool __maybe_unused, return machine__process_event(machine, event, sample); } -void thread__find_addr_map(struct thread *self, +void thread__find_addr_map(struct thread *thread, struct machine *machine, u8 cpumode, enum map_type type, u64 addr, struct addr_location *al) { - struct map_groups *mg = &self->mg; + struct map_groups *mg = &thread->mg; bool load_map = false; - al->thread = self; + al->thread = thread; al->addr = addr; al->cpumode = cpumode; al->filtered = false; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 752709ccfb00..f8d70f3003ab 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -251,7 +251,8 @@ int perf_event__process(struct perf_tool *tool, struct machine *machine); struct addr_location; -int perf_event__preprocess_sample(const union perf_event *self, + +int perf_event__preprocess_sample(const union perf_event *event, struct machine *machine, struct addr_location *al, struct perf_sample *sample); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 0617ce2a0679..e99eaed92682 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -88,7 +88,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); -union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); +union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx); void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 307f1c742563..b621347a1585 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -84,7 +84,7 @@ struct hists { u16 col_len[HISTC_NR_COLS]; }; -struct hist_entry *__hists__add_entry(struct hists *self, +struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *parent, struct branch_info *bi, @@ -93,34 +93,34 @@ struct hist_entry *__hists__add_entry(struct hists *self, int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); int hist_entry__transaction_len(void); -int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, +int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, struct hists *hists); void hist_entry__free(struct hist_entry *); -void hists__output_resort(struct hists *self); -void hists__collapse_resort(struct hists *self, struct ui_progress *prog); +void hists__output_resort(struct hists *hists); +void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); void hists__output_recalc_col_len(struct hists *hists, int max_rows); void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); -void hists__inc_nr_events(struct hists *self, u32 type); +void hists__inc_nr_events(struct hists *hists, u32 type); void events_stats__inc(struct events_stats *stats, u32 type); size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); -size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, +size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, float min_pcnt, FILE *fp); -int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); -int hist_entry__annotate(struct hist_entry *self, size_t privsize); +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); +int hist_entry__annotate(struct hist_entry *he, size_t privsize); void hists__filter_by_dso(struct hists *hists); void hists__filter_by_thread(struct hists *hists); void hists__filter_by_symbol(struct hists *hists); -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); +u16 hists__col_len(struct hists *hists, enum hist_column col); +void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); +bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len); void hists__reset_col_len(struct hists *hists); void hists__calc_col_len(struct hists *hists, struct hist_entry *he); @@ -210,12 +210,9 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, return 0; } -static inline int hist_entry__tui_annotate(struct hist_entry *self - __maybe_unused, - struct perf_evsel *evsel - __maybe_unused, - struct hist_browser_timer *hbt - __maybe_unused) +static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused, + struct perf_evsel *evsel __maybe_unused, + struct hist_browser_timer *hbt __maybe_unused) { return 0; } @@ -230,5 +227,5 @@ static inline int script_browse(const char *script_opt __maybe_unused) #define K_SWITCH_INPUT_DATA -3000 #endif -unsigned int hists__sort_list_width(struct hists *self); +unsigned int hists__sort_list_width(struct hists *hists); #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 2200dad4c3f4..ffb657ffd327 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -115,7 +115,7 @@ static const Dwfl_Callbacks offline_callbacks = { }; /* Get a Dwarf from offline image */ -static int debuginfo__init_offline_dwarf(struct debuginfo *self, +static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, const char *path) { int fd; @@ -124,25 +124,25 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *self, if (fd < 0) return fd; - self->dwfl = dwfl_begin(&offline_callbacks); - if (!self->dwfl) + dbg->dwfl = dwfl_begin(&offline_callbacks); + if (!dbg->dwfl) goto error; - self->mod = dwfl_report_offline(self->dwfl, "", "", fd); - if (!self->mod) + dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); + if (!dbg->mod) goto error; - self->dbg = dwfl_module_getdwarf(self->mod, &self->bias); - if (!self->dbg) + dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); + if (!dbg->dbg) goto error; return 0; error: - if (self->dwfl) - dwfl_end(self->dwfl); + if (dbg->dwfl) + dwfl_end(dbg->dwfl); else close(fd); - memset(self, 0, sizeof(*self)); + memset(dbg, 0, sizeof(*dbg)); return -ENOENT; } @@ -180,24 +180,24 @@ static const Dwfl_Callbacks kernel_callbacks = { }; /* Get a Dwarf from live kernel image */ -static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, +static int debuginfo__init_online_kernel_dwarf(struct debuginfo *dbg, Dwarf_Addr addr) { - self->dwfl = dwfl_begin(&kernel_callbacks); - if (!self->dwfl) + dbg->dwfl = dwfl_begin(&kernel_callbacks); + if (!dbg->dwfl) return -EINVAL; /* Load the kernel dwarves: Don't care the result here */ - dwfl_linux_kernel_report_kernel(self->dwfl); - dwfl_linux_kernel_report_modules(self->dwfl); + dwfl_linux_kernel_report_kernel(dbg->dwfl); + dwfl_linux_kernel_report_modules(dbg->dwfl); - self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); + dbg->dbg = dwfl_addrdwarf(dbg->dwfl, addr, &dbg->bias); /* Here, check whether we could get a real dwarf */ - if (!self->dbg) { + if (!dbg->dbg) { pr_debug("Failed to find kernel dwarf at %lx\n", (unsigned long)addr); - dwfl_end(self->dwfl); - memset(self, 0, sizeof(*self)); + dwfl_end(dbg->dwfl); + memset(dbg, 0, sizeof(*dbg)); return -ENOENT; } @@ -205,7 +205,7 @@ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, } #else /* With older elfutils, this just support kernel module... */ -static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, +static int debuginfo__init_online_kernel_dwarf(struct debuginfo *dbg, Dwarf_Addr addr __maybe_unused) { const char *path = kernel_get_module_path("kernel"); @@ -216,44 +216,45 @@ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, } pr_debug2("Use file %s for debuginfo\n", path); - return debuginfo__init_offline_dwarf(self, path); + return debuginfo__init_offline_dwarf(dbg, path); } #endif struct debuginfo *debuginfo__new(const char *path) { - struct debuginfo *self = zalloc(sizeof(struct debuginfo)); - if (!self) + struct debuginfo *dbg = zalloc(sizeof(*dbg)); + if (!dbg) return NULL; - if (debuginfo__init_offline_dwarf(self, path) < 0) { - free(self); - self = NULL; + if (debuginfo__init_offline_dwarf(dbg, path) < 0) { + free(dbg); + dbg = NULL; } - return self; + return dbg; } struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) { - struct debuginfo *self = zalloc(sizeof(struct debuginfo)); - if (!self) + struct debuginfo *dbg = zalloc(sizeof(*dbg)); + + if (!dbg) return NULL; - if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { - free(self); - self = NULL; + if (debuginfo__init_online_kernel_dwarf(dbg, (Dwarf_Addr)addr) < 0) { + free(dbg); + dbg = NULL; } - return self; + return dbg; } -void debuginfo__delete(struct debuginfo *self) +void debuginfo__delete(struct debuginfo *dbg) { - if (self) { - if (self->dwfl) - dwfl_end(self->dwfl); - free(self); + if (dbg) { + if (dbg->dwfl) + dwfl_end(dbg->dwfl); + free(dbg); } } @@ -1083,7 +1084,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) } /* Find probe points from debuginfo */ -static int debuginfo__find_probes(struct debuginfo *self, +static int debuginfo__find_probes(struct debuginfo *dbg, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; @@ -1094,7 +1095,7 @@ static int debuginfo__find_probes(struct debuginfo *self, #if _ELFUTILS_PREREQ(0, 142) /* Get the call frame information from this dwarf */ - pf->cfi = dwarf_getcfi(self->dbg); + pf->cfi = dwarf_getcfi(dbg->dbg); #endif off = 0; @@ -1113,7 +1114,7 @@ static int debuginfo__find_probes(struct debuginfo *self, .data = pf, }; - dwarf_getpubnames(self->dbg, pubname_search_cb, + dwarf_getpubnames(dbg->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { ret = probe_point_search_cb(&pf->sp_die, &probe_param); @@ -1123,9 +1124,9 @@ static int debuginfo__find_probes(struct debuginfo *self, } /* Loop on CUs (Compilation Unit) */ - while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { + while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { /* Get the DIE(Debugging Information Entry) of this CU */ - diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); + diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die); if (!diep) continue; @@ -1281,13 +1282,13 @@ end: } /* Find probe_trace_events specified by perf_probe_event from debuginfo */ -int debuginfo__find_trace_events(struct debuginfo *self, +int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs) { struct trace_event_finder tf = { .pf = {.pev = pev, .callback = add_probe_trace_event}, - .mod = self->mod, .max_tevs = max_tevs}; + .mod = dbg->mod, .max_tevs = max_tevs}; int ret; /* Allocate result tevs array */ @@ -1298,7 +1299,7 @@ int debuginfo__find_trace_events(struct debuginfo *self, tf.tevs = *tevs; tf.ntevs = 0; - ret = debuginfo__find_probes(self, &tf.pf); + ret = debuginfo__find_probes(dbg, &tf.pf); if (ret < 0) { free(*tevs); *tevs = NULL; @@ -1389,14 +1390,14 @@ out: } /* Find available variables at given probe point */ -int debuginfo__find_available_vars_at(struct debuginfo *self, +int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, struct variable_list **vls, int max_vls, bool externs) { struct available_var_finder af = { .pf = {.pev = pev, .callback = add_available_vars}, - .mod = self->mod, + .mod = dbg->mod, .max_vls = max_vls, .externs = externs}; int ret; @@ -1408,7 +1409,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self, af.vls = *vls; af.nvls = 0; - ret = debuginfo__find_probes(self, &af.pf); + ret = debuginfo__find_probes(dbg, &af.pf); if (ret < 0) { /* Free vlist for error */ while (af.nvls--) { @@ -1426,7 +1427,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self, } /* Reverse search */ -int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, +int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, struct perf_probe_point *ppt) { Dwarf_Die cudie, spdie, indie; @@ -1435,10 +1436,10 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, int baseline = 0, lineno = 0, ret = 0; /* Adjust address with bias */ - addr += self->bias; + addr += dbg->bias; /* Find cu die */ - if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { + if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr - dbg->bias, &cudie)) { pr_warning("Failed to find debug information for address %lx\n", addr); ret = -EINVAL; @@ -1639,7 +1640,7 @@ static int find_line_range_by_func(struct line_finder *lf) return param.retval; } -int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) +int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; int ret = 0; @@ -1656,7 +1657,7 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) struct dwarf_callback_param line_range_param = { .data = (void *)&lf, .retval = 0}; - dwarf_getpubnames(self->dbg, pubname_search_cb, + dwarf_getpubnames(dbg->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { line_range_search_cb(&lf.sp_die, &line_range_param); @@ -1667,12 +1668,12 @@ int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) /* Loop on CUs (Compilation Unit) */ while (!lf.found && ret >= 0) { - if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, + if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ - diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); + diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die); if (!diep) continue; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index d6dab0e0a937..ffc33cdd25cc 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -31,25 +31,25 @@ struct debuginfo { extern struct debuginfo *debuginfo__new(const char *path); extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); -extern void debuginfo__delete(struct debuginfo *self); +extern void debuginfo__delete(struct debuginfo *dbg); /* Find probe_trace_events specified by perf_probe_event from debuginfo */ -extern int debuginfo__find_trace_events(struct debuginfo *self, +extern int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs); /* Find a perf_probe_point from debuginfo */ -extern int debuginfo__find_probe_point(struct debuginfo *self, +extern int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, struct perf_probe_point *ppt); /* Find a line range */ -extern int debuginfo__find_line_range(struct debuginfo *self, +extern int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr); /* Find available variables */ -extern int debuginfo__find_available_vars_at(struct debuginfo *self, +extern int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, struct variable_list **vls, int max_points, bool externs); diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h index 4cedea59f518..c3cb6584d527 100644 --- a/tools/perf/util/pstack.h +++ b/tools/perf/util/pstack.h @@ -5,10 +5,10 @@ struct pstack; struct pstack *pstack__new(unsigned short max_nr_entries); -void pstack__delete(struct pstack *self); -bool pstack__empty(const struct pstack *self); -void pstack__remove(struct pstack *self, void *key); -void pstack__push(struct pstack *self, void *key); -void *pstack__pop(struct pstack *self); +void pstack__delete(struct pstack *pstack); +bool pstack__empty(const struct pstack *pstack); +void pstack__remove(struct pstack *pstack, void *key); +void pstack__push(struct pstack *pstack, void *key); +void *pstack__pop(struct pstack *pstack); #endif /* _PERF_PSTACK_ */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 3c1b30103d54..0ce46943d627 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -16,11 +16,11 @@ #include "perf_regs.h" #include "vdso.h" -static int perf_session__open(struct perf_session *self) +static int perf_session__open(struct perf_session *session) { - struct perf_data_file *file = self->file; + struct perf_data_file *file = session->file; - if (perf_session__read_header(self) < 0) { + if (perf_session__read_header(session) < 0) { pr_err("incompatible file format (rerun with -v to learn more)"); return -1; } @@ -28,17 +28,17 @@ static int perf_session__open(struct perf_session *self) if (perf_data_file__is_pipe(file)) return 0; - if (!perf_evlist__valid_sample_type(self->evlist)) { + if (!perf_evlist__valid_sample_type(session->evlist)) { pr_err("non matching sample_type"); return -1; } - if (!perf_evlist__valid_sample_id_all(self->evlist)) { + if (!perf_evlist__valid_sample_id_all(session->evlist)) { pr_err("non matching sample_id_all"); return -1; } - if (!perf_evlist__valid_read_format(self->evlist)) { + if (!perf_evlist__valid_read_format(session->evlist)) { pr_err("non matching read_format"); return -1; } @@ -53,46 +53,45 @@ void perf_session__set_id_hdr_size(struct perf_session *session) machines__set_id_hdr_size(&session->machines, id_hdr_size); } -int perf_session__create_kernel_maps(struct perf_session *self) +int perf_session__create_kernel_maps(struct perf_session *session) { - int ret = machine__create_kernel_maps(&self->machines.host); + int ret = machine__create_kernel_maps(&session->machines.host); if (ret >= 0) - ret = machines__create_guest_kernel_maps(&self->machines); + ret = machines__create_guest_kernel_maps(&session->machines); return ret; } -static void perf_session__destroy_kernel_maps(struct perf_session *self) +static void perf_session__destroy_kernel_maps(struct perf_session *session) { - machines__destroy_kernel_maps(&self->machines); + machines__destroy_kernel_maps(&session->machines); } struct perf_session *perf_session__new(struct perf_data_file *file, bool repipe, struct perf_tool *tool) { - struct perf_session *self; + struct perf_session *session = zalloc(sizeof(*session)); - self = zalloc(sizeof(*self)); - if (!self) + if (!session) goto out; - self->repipe = repipe; - INIT_LIST_HEAD(&self->ordered_samples.samples); - INIT_LIST_HEAD(&self->ordered_samples.sample_cache); - INIT_LIST_HEAD(&self->ordered_samples.to_free); - machines__init(&self->machines); + session->repipe = repipe; + INIT_LIST_HEAD(&session->ordered_samples.samples); + INIT_LIST_HEAD(&session->ordered_samples.sample_cache); + INIT_LIST_HEAD(&session->ordered_samples.to_free); + machines__init(&session->machines); if (file) { if (perf_data_file__open(file)) goto out_delete; - self->file = file; + session->file = file; if (perf_data_file__is_read(file)) { - if (perf_session__open(self) < 0) + if (perf_session__open(session) < 0) goto out_close; - perf_session__set_id_hdr_size(self); + perf_session__set_id_hdr_size(session); } } @@ -101,22 +100,22 @@ struct perf_session *perf_session__new(struct perf_data_file *file, * In O_RDONLY mode this will be performed when reading the * kernel MMAP event, in perf_event__process_mmap(). */ - if (perf_session__create_kernel_maps(self) < 0) + if (perf_session__create_kernel_maps(session) < 0) goto out_delete; } if (tool && tool->ordering_requires_timestamps && - tool->ordered_samples && !perf_evlist__sample_id_all(self->evlist)) { + tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) { dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); tool->ordered_samples = false; } - return self; + return session; out_close: perf_data_file__close(file); out_delete: - perf_session__delete(self); + perf_session__delete(session); out: return NULL; } @@ -147,16 +146,16 @@ static void perf_session_env__delete(struct perf_session_env *env) free(env->pmu_mappings); } -void perf_session__delete(struct perf_session *self) +void perf_session__delete(struct perf_session *session) { - perf_session__destroy_kernel_maps(self); - perf_session__delete_dead_threads(self); - perf_session__delete_threads(self); - perf_session_env__delete(&self->header.env); - machines__exit(&self->machines); - if (self->file) - perf_data_file__close(self->file); - free(self); + perf_session__destroy_kernel_maps(session); + perf_session__delete_dead_threads(session); + perf_session__delete_threads(session); + perf_session_env__delete(&session->header.env); + machines__exit(&session->machines); + if (session->file) + perf_data_file__close(session->file); + free(session); vdso__exit(); } @@ -1084,11 +1083,11 @@ static int perf_session__process_event(struct perf_session *session, file_offset); } -void perf_event_header__bswap(struct perf_event_header *self) +void perf_event_header__bswap(struct perf_event_header *hdr) { - self->type = bswap_32(self->type); - self->misc = bswap_16(self->misc); - self->size = bswap_16(self->size); + hdr->type = bswap_32(hdr->type); + hdr->misc = bswap_16(hdr->misc); + hdr->size = bswap_16(hdr->size); } struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) @@ -1096,9 +1095,9 @@ struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) return machine__findnew_thread(&session->machines.host, 0, pid); } -static struct thread *perf_session__register_idle_thread(struct perf_session *self) +static struct thread *perf_session__register_idle_thread(struct perf_session *session) { - struct thread *thread = perf_session__findnew(self, 0); + struct thread *thread = perf_session__findnew(session, 0); if (thread == NULL || thread__set_comm(thread, "swapper", 0)) { pr_err("problem inserting idle task.\n"); @@ -1150,10 +1149,10 @@ static void perf_session__warn_about_errors(const struct perf_session *session, volatile int session_done; -static int __perf_session__process_pipe_events(struct perf_session *self, +static int __perf_session__process_pipe_events(struct perf_session *session, struct perf_tool *tool) { - int fd = perf_data_file__fd(self->file); + int fd = perf_data_file__fd(session->file); union perf_event *event; uint32_t size, cur_size = 0; void *buf = NULL; @@ -1181,7 +1180,7 @@ more: goto out_err; } - if (self->header.needs_swap) + if (session->header.needs_swap) perf_event_header__bswap(&event->header); size = event->header.size; @@ -1216,7 +1215,7 @@ more: } } - if ((skip = perf_session__process_event(self, event, tool, head)) < 0) { + if ((skip = perf_session__process_event(session, event, tool, head)) < 0) { pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", head, event->header.size, event->header.type); err = -EINVAL; @@ -1232,12 +1231,12 @@ more: goto more; done: /* do the final flush for ordered samples */ - self->ordered_samples.next_flush = ULLONG_MAX; - err = flush_sample_queue(self, tool); + session->ordered_samples.next_flush = ULLONG_MAX; + err = flush_sample_queue(session, tool); out_err: free(buf); - perf_session__warn_about_errors(self, tool); - perf_session_free_sample_buffers(self); + perf_session__warn_about_errors(session, tool); + perf_session_free_sample_buffers(session); return err; } @@ -1377,22 +1376,22 @@ out_err: return err; } -int perf_session__process_events(struct perf_session *self, +int perf_session__process_events(struct perf_session *session, struct perf_tool *tool) { - u64 size = perf_data_file__size(self->file); + u64 size = perf_data_file__size(session->file); int err; - if (perf_session__register_idle_thread(self) == NULL) + if (perf_session__register_idle_thread(session) == NULL) return -ENOMEM; - if (!perf_data_file__is_pipe(self->file)) - err = __perf_session__process_events(self, - self->header.data_offset, - self->header.data_size, + if (!perf_data_file__is_pipe(session->file)) + err = __perf_session__process_events(session, + session->header.data_offset, + session->header.data_size, size, tool); else - err = __perf_session__process_pipe_events(self, tool); + err = __perf_session__process_pipe_events(session, tool); return err; } @@ -1441,15 +1440,15 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps, return 0; } -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) +size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp) { - return machines__fprintf_dsos(&self->machines, fp); + return machines__fprintf_dsos(&session->machines, fp); } -size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, +size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, bool (skip)(struct dso *dso, int parm), int parm) { - return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm); + return machines__fprintf_dsos_buildid(&session->machines, fp, skip, parm); } size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 27c74d38b868..50f640958f0f 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -51,12 +51,12 @@ struct perf_session *perf_session__new(struct perf_data_file *file, bool repipe, struct perf_tool *tool); void perf_session__delete(struct perf_session *session); -void perf_event_header__bswap(struct perf_event_header *self); +void perf_event_header__bswap(struct perf_event_header *hdr); -int __perf_session__process_events(struct perf_session *self, +int __perf_session__process_events(struct perf_session *session, u64 data_offset, u64 data_size, u64 size, struct perf_tool *tool); -int perf_session__process_events(struct perf_session *self, +int perf_session__process_events(struct perf_session *session, struct perf_tool *tool); int perf_session_queue_event(struct perf_session *s, union perf_event *event, @@ -64,37 +64,38 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event, void perf_tool__fill_defaults(struct perf_tool *tool); -int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel, +int perf_session__resolve_callchain(struct perf_session *session, + struct perf_evsel *evsel, struct thread *thread, struct ip_callchain *chain, struct symbol **parent); -bool perf_session__has_traces(struct perf_session *self, const char *msg); +bool perf_session__has_traces(struct perf_session *session, const char *msg); void mem_bswap_64(void *src, int byte_size); void mem_bswap_32(void *src, int byte_size); void perf_event__attr_swap(struct perf_event_attr *attr); -int perf_session__create_kernel_maps(struct perf_session *self); +int perf_session__create_kernel_maps(struct perf_session *session); void perf_session__set_id_hdr_size(struct perf_session *session); static inline -struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) +struct machine *perf_session__find_machine(struct perf_session *session, pid_t pid) { - return machines__find(&self->machines, pid); + return machines__find(&session->machines, pid); } static inline -struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) +struct machine *perf_session__findnew_machine(struct perf_session *session, pid_t pid) { - return machines__findnew(&self->machines, pid); + return machines__findnew(&session->machines, pid); } -struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); -size_t perf_session__fprintf(struct perf_session *self, FILE *fp); +struct thread *perf_session__findnew(struct perf_session *session, pid_t pid); +size_t perf_session__fprintf(struct perf_session *session, FILE *fp); -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); +size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp); size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, bool (fn)(struct dso *dso, int parm), int parm); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index f4e16f359d64..f4cc147e0220 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -180,7 +180,7 @@ struct sort_entry { int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); - int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, + int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, unsigned int width); u8 se_width_idx; bool elide; diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 67e4a0082822..3edd0538161f 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -62,15 +62,15 @@ static struct strfilter_node *strfilter_node__alloc(const char *op, struct strfilter_node *l, struct strfilter_node *r) { - struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node)); + struct strfilter_node *node = zalloc(sizeof(*node)); - if (ret) { - ret->p = op; - ret->l = l; - ret->r = r; + if (node) { + node->p = op; + node->l = l; + node->r = r; } - return ret; + return node; } static struct strfilter_node *strfilter_node__new(const char *s, @@ -154,20 +154,20 @@ error: */ struct strfilter *strfilter__new(const char *rules, const char **err) { - struct strfilter *ret = zalloc(sizeof(struct strfilter)); + struct strfilter *filter = zalloc(sizeof(*filter)); const char *ep = NULL; - if (ret) - ret->root = strfilter_node__new(rules, &ep); + if (filter) + filter->root = strfilter_node__new(rules, &ep); - if (!ret || !ret->root || *ep != '\0') { + if (!filter || !filter->root || *ep != '\0') { if (err) *err = ep; - strfilter__delete(ret); - ret = NULL; + strfilter__delete(filter); + filter = NULL; } - return ret; + return filter; } static bool strfilter_node__compare(struct strfilter_node *node, @@ -191,9 +191,9 @@ static bool strfilter_node__compare(struct strfilter_node *node, } /* Return true if STR matches the filter rules */ -bool strfilter__compare(struct strfilter *node, const char *str) +bool strfilter__compare(struct strfilter *filter, const char *str) { - if (!node) + if (!filter) return false; - return strfilter_node__compare(node->root, str); + return strfilter_node__compare(filter->root, str); } diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h index 00f58a7506de..fe611f3c9e39 100644 --- a/tools/perf/util/strfilter.h +++ b/tools/perf/util/strfilter.h @@ -30,19 +30,19 @@ struct strfilter *strfilter__new(const char *rules, const char **err); /** * strfilter__compare - compare given string and a string filter - * @self: String filter + * @filter: String filter * @str: target string * - * Compare @str and @self. Return true if the str match the rule + * Compare @str and @filter. Return true if the str match the rule */ -bool strfilter__compare(struct strfilter *self, const char *str); +bool strfilter__compare(struct strfilter *filter, const char *str); /** * strfilter__delete - delete a string filter - * @self: String filter to delete + * @filter: String filter to delete * - * Delete @self. + * Delete @filter. */ -void strfilter__delete(struct strfilter *self); +void strfilter__delete(struct strfilter *filter); #endif diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 373c055989ed..897c1b2a750a 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -29,24 +29,24 @@ struct machine; struct comm; struct thread *thread__new(pid_t pid, pid_t tid); -void thread__delete(struct thread *self); +void thread__delete(struct thread *thread); static inline void thread__exited(struct thread *thread) { thread->dead = true; } int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); -int thread__comm_len(struct thread *self); +int thread__comm_len(struct thread *thread); struct comm *thread__comm(const struct thread *thread); const char *thread__comm_str(const struct thread *thread); -void thread__insert_map(struct thread *self, struct map *map); +void thread__insert_map(struct thread *thread, struct map *map); int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); size_t thread__fprintf(struct thread *thread, FILE *fp); -static inline struct map *thread__find_map(struct thread *self, +static inline struct map *thread__find_map(struct thread *thread, enum map_type type, u64 addr) { - return self ? map_groups__find(&self->mg, type, addr) : NULL; + return thread ? map_groups__find(&thread->mg, type, addr) : NULL; } void thread__find_addr_map(struct thread *thread, struct machine *machine, -- cgit v1.2.2 From a94d342b9cb09edfe888ea972af0883b6a8d992b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2013 11:42:46 +0100 Subject: tools/perf: Add required memory barriers To match patch bf378d341e48 ("perf: Fix perf ring buffer memory ordering") change userspace to also adhere to the ordering outlined. Signed-off-by: Peter Zijlstra Cc: Michael Neuling Cc: "Paul E. McKenney" Cc: james.hogan@imgtec.com Cc: Vince Weaver Cc: Victor Kaplansky Cc: Oleg Nesterov Cc: Anton Blanchard Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Mathieu Desnoyers Cc: Michael Ellerman Link: http://lkml.kernel.org/r/20131030104246.GH16117@laptop.programming.kicks-ass.net Signed-off-by: Ingo Molnar --- tools/perf/util/evlist.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index e99eaed92682..ecaa582f40e2 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -177,7 +177,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, s static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) { struct perf_event_mmap_page *pc = mm->base; - int head = pc->data_head; + int head = ACCESS_ONCE(pc->data_head); rmb(); return head; } @@ -190,7 +190,7 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md, /* * ensure all reads are done before we write the tail out. */ - /* mb(); */ + mb(); pc->data_tail = tail; } -- cgit v1.2.2 From 744a971940520cf0818e1fe882b64892c528e6de Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 6 Nov 2013 10:17:38 -0300 Subject: perf evsel: Ditch evsel->handler.data field Not needed since this cset: fcf65bf149af: perf evsel: Cache associated event_format So lets trim this struct a bit. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-j8setslokt0goiwxq9dogzqm@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 2 +- tools/perf/util/evsel.h | 5 +---- tools/perf/util/session.c | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 1c173ccb0ef2..b939221efd8d 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -255,7 +255,7 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist, if (evsel == NULL) return -1; - evsel->handler.func = handler; + evsel->handler = handler; perf_evlist__add(evlist, evsel); return 0; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 5aa68cddc7d9..64ec8e1a7a28 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -74,10 +74,7 @@ struct perf_evsel { off_t id_offset; }; struct cgroup_sel *cgrp; - struct { - void *func; - void *data; - } handler; + void *handler; struct cpu_map *cpus; unsigned int sample_size; int id_pos; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0ce46943d627..f36d24a02445 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1650,9 +1650,9 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, continue; err = -EEXIST; - if (evsel->handler.func != NULL) + if (evsel->handler != NULL) goto out; - evsel->handler.func = assocs[i].handler; + evsel->handler = assocs[i].handler; } err = 0; -- cgit v1.2.2 From a614d01bdd0cc8200d917da25f5a3d539b944193 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 6 Nov 2013 08:55:35 -0700 Subject: perf tools: Fix version when building out of tree When building perf out of tree: $ make perf-tar-src-pkg $ tar -xf perf-.tar -C /tmp $ cd /tmp/perf $ make -C tools/perf you get this warning message: make[1]: *** No rule to make target `kernelversion'. Stop. Fix it by saving the perf version in the tar file and using that for the out of tree builds. v2: removed short form request and fixed up version string from usual output. Signed-off-by: David Ahern Suggested-by: Ingo Molnar Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1383753335-25782-1-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/PERF-VERSION-GEN | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index ce7a804b2951..39f17507578d 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -19,6 +19,9 @@ if test -d ../../.git -o -f ../../.git then TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null ) CID=$(git log -1 --abbrev=4 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID" +elif test -f ../../PERF-VERSION-FILE +then + TAG=$(cut -d' ' -f3 ../../PERF-VERSION-FILE | sed -e 's/\"//g') fi if test -z "$TAG" then -- cgit v1.2.2 From 8ce000e83848578a621d64eccdc88bd34c2fc70c Mon Sep 17 00:00:00 2001 From: Rodrigo Campos Date: Wed, 6 Nov 2013 22:20:54 +0000 Subject: perf tools: Remove unneeded include There is no point in sort.h including itself. The include was added when the file was created, in commit "perf tools: Create util/sort.and use it" (dd68ada2d) and added a include to "sort.h" in lot of files (all the files that started using the file). It was probably added by mistake on sort.h too. Signed-off-by: Rodrigo Campos Acked-by: Namhyung Kim Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383776454-10595-1-git-send-email-rodrigo@sdfg.com.ar Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.h | 1 - 1 file changed, 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index f4cc147e0220..43e5ff42a609 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -22,7 +22,6 @@ #include "parse-events.h" #include "thread.h" -#include "sort.h" extern regex_t parent_regex; extern const char *sort_order; -- cgit v1.2.2 From ef503831d8d64e12c6dad5547875cfcd4c5d043c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Nov 2013 16:41:19 -0300 Subject: perf evsel: Remove idx parm from constructor Most uses of the evsel constructor are followed by a call to perf_evlist__add with an idex of evlist->nr_entries, so make rename the current constructor to perf_evsel__new_idx and remove the need for passing the constructor for the common case. We still need the new_idx variant because the way groups are handled, with evsel->nr_members holding the number of entries in an evlist, partitioning the evlist into sublists inside a single linked list. This asks for a clarifying refactoring, but for now simplify the non parser cases, so that tool writers don't have to bother with evsel idx setting. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-zy9tskx6jqm2rmw7468zze2a@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 9 +++++---- tools/perf/util/evsel.c | 4 ++-- tools/perf/util/evsel.h | 15 +++++++++++++-- tools/perf/util/header.c | 4 ++-- tools/perf/util/parse-events.c | 6 +++--- 5 files changed, 25 insertions(+), 13 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b939221efd8d..99dc58e5dcc3 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -117,6 +117,8 @@ void perf_evlist__delete(struct perf_evlist *evlist) void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) { list_add_tail(&entry->node, &evlist->entries); + entry->idx = evlist->nr_entries; + if (!evlist->nr_entries++) perf_evlist__set_id_pos(evlist); } @@ -165,7 +167,7 @@ int perf_evlist__add_default(struct perf_evlist *evlist) event_attr_init(&attr); - evsel = perf_evsel__new(&attr, 0); + evsel = perf_evsel__new(&attr); if (evsel == NULL) goto error; @@ -190,7 +192,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist, size_t i; for (i = 0; i < nr_attrs; i++) { - evsel = perf_evsel__new(attrs + i, evlist->nr_entries + i); + evsel = perf_evsel__new_idx(attrs + i, evlist->nr_entries + i); if (evsel == NULL) goto out_delete_partial_list; list_add_tail(&evsel->node, &head); @@ -249,9 +251,8 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, int perf_evlist__add_newtp(struct perf_evlist *evlist, const char *sys, const char *name, void *handler) { - struct perf_evsel *evsel; + struct perf_evsel *evsel = perf_evsel__newtp(sys, name); - evsel = perf_evsel__newtp(sys, name, evlist->nr_entries); if (evsel == NULL) return -1; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 5280820ed389..f95653a639a6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -168,7 +168,7 @@ void perf_evsel__init(struct perf_evsel *evsel, perf_evsel__calc_id_pos(evsel); } -struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) +struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) { struct perf_evsel *evsel = zalloc(sizeof(*evsel)); @@ -219,7 +219,7 @@ out: return format; } -struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx) +struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) { struct perf_evsel *evsel = zalloc(sizeof(*evsel)); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 64ec8e1a7a28..0178233abd64 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -96,8 +96,19 @@ struct thread_map; struct perf_evlist; struct perf_record_opts; -struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); -struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx); +struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); + +static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) +{ + return perf_evsel__new_idx(attr, 0); +} + +struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx); + +static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name) +{ + return perf_evsel__newtp_idx(sys, name, 0); +} struct event_format *event_format__new(const char *sys, const char *name); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 26d9520a0c1b..369c03648f88 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2797,7 +2797,7 @@ int perf_session__read_header(struct perf_session *session) perf_event__attr_swap(&f_attr.attr); tmp = lseek(fd, 0, SEEK_CUR); - evsel = perf_evsel__new(&f_attr.attr, i); + evsel = perf_evsel__new(&f_attr.attr); if (evsel == NULL) goto out_delete_evlist; @@ -2916,7 +2916,7 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, return -ENOMEM; } - evsel = perf_evsel__new(&event->attr.attr, evlist->nr_entries); + evsel = perf_evsel__new(&event->attr.attr); if (evsel == NULL) return -ENOMEM; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c90e55cf7e82..6de6f89c2a61 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -277,7 +277,7 @@ static int __add_event(struct list_head *list, int *idx, event_attr_init(attr); - evsel = perf_evsel__new(attr, (*idx)++); + evsel = perf_evsel__new_idx(attr, (*idx)++); if (!evsel) return -ENOMEM; @@ -378,7 +378,7 @@ static int add_tracepoint(struct list_head *list, int *idx, { struct perf_evsel *evsel; - evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++); + evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++); if (!evsel) return -ENOMEM; @@ -1097,7 +1097,7 @@ static bool is_event_supported(u8 type, unsigned config) .threads = { 0 }, }; - evsel = perf_evsel__new(&attr, 0); + evsel = perf_evsel__new(&attr); if (evsel) { ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; perf_evsel__delete(evsel); -- cgit v1.2.2 From 62605dc50c27bf0e4ff69b7b3166f226586aff02 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 11 Nov 2013 09:44:09 -0300 Subject: perf record: Synthesize non-exec MMAP records when --data used When perf_event_attr.mmap_data is set the kernel will generate PERF_RECORD_MMAP events when non-exec (data, SysV mem) mmaps are created, so we need to synthesize from /proc/pid/maps for existing threads, as we do for exec mmaps. Right now just 'perf record' does it, but any other tool that uses perf_event__synthesize_thread(s|map) can request it. Reported-by: Don Zickus Tested-by: Don Zickus Cc: Adrian Hunter Cc: Bill Gray Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Joe Mario Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Richard Fowles Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-ihwzraikx23ian9txinogvv2@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 50 +++++++++++++++++++++++++++++-------------------- tools/perf/util/event.h | 4 ++-- 2 files changed, 32 insertions(+), 22 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index ec9ae1114ed4..6e3a846aed0e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -170,7 +170,8 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, - struct machine *machine) + struct machine *machine, + bool mmap_data) { char filename[PATH_MAX]; FILE *fp; @@ -188,10 +189,6 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, } event->header.type = PERF_RECORD_MMAP; - /* - * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c - */ - event->header.misc = PERF_RECORD_MISC_USER; while (1) { char bf[BUFSIZ]; @@ -215,9 +212,17 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, if (n != 5) continue; + /* + * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c + */ + event->header.misc = PERF_RECORD_MISC_USER; - if (prot[2] != 'x') - continue; + if (prot[2] != 'x') { + if (!mmap_data || prot[0] != 'r') + continue; + + event->header.misc |= PERF_RECORD_MISC_MMAP_DATA; + } if (!strcmp(execname, "")) strcpy(execname, anonstr); @@ -304,20 +309,21 @@ static int __event__synthesize_thread(union perf_event *comm_event, pid_t pid, int full, perf_event__handler_t process, struct perf_tool *tool, - struct machine *machine) + struct machine *machine, bool mmap_data) { pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, process, machine); if (tgid == -1) return -1; return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, - process, machine); + process, machine, mmap_data); } int perf_event__synthesize_thread_map(struct perf_tool *tool, struct thread_map *threads, perf_event__handler_t process, - struct machine *machine) + struct machine *machine, + bool mmap_data) { union perf_event *comm_event, *mmap_event; int err = -1, thread, j; @@ -334,7 +340,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, for (thread = 0; thread < threads->nr; ++thread) { if (__event__synthesize_thread(comm_event, mmap_event, threads->map[thread], 0, - process, tool, machine)) { + process, tool, machine, + mmap_data)) { err = -1; break; } @@ -356,10 +363,10 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, /* if not, generate events for it */ if (need_leader && - __event__synthesize_thread(comm_event, - mmap_event, - comm_event->comm.pid, 0, - process, tool, machine)) { + __event__synthesize_thread(comm_event, mmap_event, + comm_event->comm.pid, 0, + process, tool, machine, + mmap_data)) { err = -1; break; } @@ -374,7 +381,7 @@ out: int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, - struct machine *machine) + struct machine *machine, bool mmap_data) { DIR *proc; struct dirent dirent, *next; @@ -404,7 +411,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, * one thread couldn't be synthesized. */ __event__synthesize_thread(comm_event, mmap_event, pid, 1, - process, tool, machine); + process, tool, machine, mmap_data); } err = 0; @@ -528,19 +535,22 @@ int perf_event__process_lost(struct perf_tool *tool __maybe_unused, size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) { - return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", + return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n", event->mmap.pid, event->mmap.tid, event->mmap.start, - event->mmap.len, event->mmap.pgoff, event->mmap.filename); + event->mmap.len, event->mmap.pgoff, + (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', + event->mmap.filename); } size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) { return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 - " %02x:%02x %"PRIu64" %"PRIu64"]: %s\n", + " %02x:%02x %"PRIu64" %"PRIu64"]: %c %s\n", event->mmap2.pid, event->mmap2.tid, event->mmap2.start, event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, event->mmap2.min, event->mmap2.ino, event->mmap2.ino_generation, + (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', event->mmap2.filename); } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index f8d70f3003ab..30fec9901e44 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -208,10 +208,10 @@ typedef int (*perf_event__handler_t)(struct perf_tool *tool, int perf_event__synthesize_thread_map(struct perf_tool *tool, struct thread_map *threads, perf_event__handler_t process, - struct machine *machine); + struct machine *machine, bool mmap_data); int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, - struct machine *machine); + struct machine *machine, bool mmap_data); int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, -- cgit v1.2.2 From 58d925dcede9e8765876707a33a3406011fe1c11 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 11 Nov 2013 11:28:02 -0300 Subject: perf machine: Introduce synthesize_threads method out of open coded equivalent Further simplifications to be done on following patch, as most tools don't use the callback, using instead just the canned machine__process_event one. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-r1m0vuuj3cat4bampno9yc8d@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 12 ++++++++++++ tools/perf/util/machine.h | 4 ++++ 2 files changed, 16 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ce034c183a7e..9f2c61d5a9ed 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1394,3 +1394,15 @@ int machine__for_each_thread(struct machine *machine, } return rc; } + +int machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, + struct perf_target *target, struct thread_map *threads, + perf_event__handler_t process, bool data_mmap) +{ + if (perf_target__has_task(target)) + return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap); + else if (perf_target__has_cpu(target)) + return perf_event__synthesize_threads(tool, process, machine, data_mmap); + /* command specified */ + return 0; +} diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 2389ba81fafe..14a89d2aecaf 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -4,6 +4,7 @@ #include #include #include "map.h" +#include "event.h" struct addr_location; struct branch_stack; @@ -178,4 +179,7 @@ int machine__for_each_thread(struct machine *machine, int (*fn)(struct thread *thread, void *p), void *priv); +int machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, + struct perf_target *target, struct thread_map *threads, + perf_event__handler_t process, bool data_mmap); #endif /* __PERF_MACHINE_H */ -- cgit v1.2.2 From a33fbd56ec83b5421090b4d8f2032f635e6a9488 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 11 Nov 2013 11:36:12 -0300 Subject: perf machine: Simplify synthesize_threads method Several tools (top, kvm) don't need to be called back to process each of the syntheiszed records, instead relying on the machine__process_event function to change the per machine data structures that represent threads and mmaps, so provide a way to ask for this common idiom. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-pusqibp8n3c4ynegd1frn4zd@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 6 +++--- tools/perf/util/machine.h | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 9f2c61d5a9ed..680700b6d779 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1395,9 +1395,9 @@ int machine__for_each_thread(struct machine *machine, return rc; } -int machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, - struct perf_target *target, struct thread_map *threads, - perf_event__handler_t process, bool data_mmap) +int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, + struct perf_target *target, struct thread_map *threads, + perf_event__handler_t process, bool data_mmap) { if (perf_target__has_task(target)) return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap); diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 14a89d2aecaf..fedd1dfaf715 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -179,7 +179,15 @@ int machine__for_each_thread(struct machine *machine, int (*fn)(struct thread *thread, void *p), void *priv); -int machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, - struct perf_target *target, struct thread_map *threads, - perf_event__handler_t process, bool data_mmap); +int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, + struct perf_target *target, struct thread_map *threads, + perf_event__handler_t process, bool data_mmap); +static inline +int machine__synthesize_threads(struct machine *machine, struct perf_target *target, + struct thread_map *threads, bool data_mmap) +{ + return __machine__synthesize_threads(machine, NULL, target, threads, + perf_event__process, data_mmap); +} + #endif /* __PERF_MACHINE_H */ -- cgit v1.2.2 From 7524f63b997cc02a80aa073558728ae3ee242cf8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 8 Nov 2013 17:53:42 +0900 Subject: perf tools: Prevent condition that all sort keys are elided If given sort keys are all elided there'll be no output except for the overhead column - actually the TUI shows a noisy output. In this case it'd be better to show up the sort keys rather than elide. Before: $ perf report -s comm -c perf (...) # Overhead # ........ # 100.00% After: $ perf report -s comm -c perf (...) # Overhead Command # ........ ....... # 100.00% perf Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1383900822-14609-1-git-send-email-namhyung@kernel.org [ Us curly braces around multi-line statements, as requested by Ingo Molnar ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 3c1b75c8b9a6..8b0bb1f4494a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1137,6 +1137,8 @@ static void sort_entry__setup_elide(struct sort_entry *se, void sort__setup_elide(FILE *output) { + struct sort_entry *se; + sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", output); sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, @@ -1172,4 +1174,15 @@ void sort__setup_elide(FILE *output) "snoop", output); } + /* + * It makes no sense to elide all of sort entries. + * Just revert them to show up again. + */ + list_for_each_entry(se, &hist_entry__sort_list, list) { + if (!se->elide) + return; + } + + list_for_each_entry(se, &hist_entry__sort_list, list) + se->elide = false; } -- cgit v1.2.2 From 8973504be70b2986a2081eeff7d9a4210dec295d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 12 Nov 2013 07:46:53 -0700 Subject: perf record: Fix segfault with --no-mmap-pages Adrian reported a segfault when using --no-out-pages: $ tools/perf/perf record -vv --no-out-pages uname Segmentation fault (core dumped) The same occurs with --no-mmap-pages. Fix by checking that str is non-NULL before parsing it. Signed-off-by: David Ahern Reported-by: Adrian Hunter Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1384267617-3446-2-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 99dc58e5dcc3..3960560f873a 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -719,6 +719,9 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, { .tag = 0 }, }; + if (str == NULL) + return -1; + val = parse_tag_value(str, tags); if (val != (unsigned long) -1) { /* we got file size value */ -- cgit v1.2.2 From 9639837e95db90d056f4683c911717921519320e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 12 Nov 2013 07:46:54 -0700 Subject: perf evlist: Round mmap pages to power 2 - v2 Currently perf requires the -m / --mmap_pages option to be a power of 2. To be more user friendly perf should automatically round this up to the next power of 2. Currently: $ perf record -m 3 -a -- sleep 1 --mmap_pages/-m value must be a power of two.sleep: Terminated With patch: $ perf record -m 3 -a -- sleep 1 rounding mmap pages size to 16384 (4 pages) ... v2: Add bytes units to rounding message per Ingo's request. Other suggestions (e.g., prefixing INFO) should be addressed by wrapping pr_info to catch all instances. Suggested-by: Ingo Molnar Signed-off-by: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1384267617-3446-3-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 3960560f873a..fb4727d3df85 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -710,7 +710,6 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, { unsigned int *mmap_pages = opt->value; unsigned long pages, val; - size_t size; static struct parse_tag tags[] = { { .tag = 'B', .mult = 1 }, { .tag = 'K', .mult = 1 << 10 }, @@ -726,11 +725,6 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, if (val != (unsigned long) -1) { /* we got file size value */ pages = PERF_ALIGN(val, page_size) / page_size; - if (pages < (1UL << 31) && !is_power_of_2(pages)) { - pages = next_pow2(pages); - pr_info("rounding mmap pages size to %lu (%lu pages)\n", - pages * page_size, pages); - } } else { /* we got pages count value */ char *eptr; @@ -741,14 +735,14 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, } } - if (pages > UINT_MAX || pages > SIZE_MAX / page_size) { - pr_err("--mmap_pages/-m value too big\n"); - return -1; + if (pages < (1UL << 31) && !is_power_of_2(pages)) { + pages = next_pow2(pages); + pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", + pages * page_size, pages); } - size = perf_evlist__mmap_size(pages); - if (!size) { - pr_err("--mmap_pages/-m value must be a power of two."); + if (pages > UINT_MAX || pages > SIZE_MAX / page_size) { + pr_err("--mmap_pages/-m value too big\n"); return -1; } -- cgit v1.2.2 From 33c2dcfdfe7f114cc656bcb4c839f5939d5e60ba Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 12 Nov 2013 07:46:55 -0700 Subject: perf evlist: Refactor mmap_pages parsing Logic will be re-used for the out-pages argument for mmap based writes in perf-record. Signed-off-by: David Ahern Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1384267617-3446-4-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index fb4727d3df85..cb19044601bb 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -705,10 +705,9 @@ static size_t perf_evlist__mmap_size(unsigned long pages) return (pages + 1) * page_size; } -int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, - int unset __maybe_unused) +static long parse_pages_arg(const char *str, unsigned long min, + unsigned long max) { - unsigned int *mmap_pages = opt->value; unsigned long pages, val; static struct parse_tag tags[] = { { .tag = 'B', .mult = 1 }, @@ -719,7 +718,7 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, }; if (str == NULL) - return -1; + return -EINVAL; val = parse_tag_value(str, tags); if (val != (unsigned long) -1) { @@ -729,20 +728,38 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, /* we got pages count value */ char *eptr; pages = strtoul(str, &eptr, 10); - if (*eptr != '\0') { - pr_err("failed to parse --mmap_pages/-m value\n"); - return -1; - } + if (*eptr != '\0') + return -EINVAL; } - if (pages < (1UL << 31) && !is_power_of_2(pages)) { + if ((pages == 0) && (min == 0)) { + /* leave number of pages at 0 */ + } else if (pages < (1UL << 31) && !is_power_of_2(pages)) { + /* round pages up to next power of 2 */ pages = next_pow2(pages); pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", pages * page_size, pages); } - if (pages > UINT_MAX || pages > SIZE_MAX / page_size) { - pr_err("--mmap_pages/-m value too big\n"); + if (pages > max) + return -EINVAL; + + return pages; +} + +int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, + int unset __maybe_unused) +{ + unsigned int *mmap_pages = opt->value; + unsigned long max = UINT_MAX; + long pages; + + if (max < SIZE_MAX / page_size) + max = SIZE_MAX / page_size; + + pages = parse_pages_arg(str, 1, max); + if (pages < 0) { + pr_err("Invalid argument for --mmap_pages/-m\n"); return -1; } -- cgit v1.2.2 From 602ad878d41ef097cc9aa2def7830d5bb27a15d8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 12 Nov 2013 16:46:16 -0300 Subject: perf target: Shorten perf_target__ to target__ Getting unwieldly long, for this app domain should be descriptive enough and the use of __ to separate the class from the method names should help with avoiding clashes with other code bases. Reported-by: David Ahern Suggested-by: Ingo Molnar Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/20131112113427.GA4053@ghostprotocols.net Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 12 +++++------ tools/perf/util/evlist.h | 5 ++--- tools/perf/util/evsel.c | 9 ++++---- tools/perf/util/evsel.h | 3 +-- tools/perf/util/machine.c | 6 +++--- tools/perf/util/machine.h | 4 ++-- tools/perf/util/target.c | 54 +++++++++++++++++++++++------------------------ tools/perf/util/target.h | 44 ++++++++++++++++++-------------------- tools/perf/util/top.c | 2 +- 9 files changed, 65 insertions(+), 74 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index cb19044601bb..dc6fa3fbb180 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -811,8 +811,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, return perf_evlist__mmap_per_cpu(evlist, prot, mask); } -int perf_evlist__create_maps(struct perf_evlist *evlist, - struct perf_target *target) +int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) { evlist->threads = thread_map__new_str(target->pid, target->tid, target->uid); @@ -820,9 +819,9 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, if (evlist->threads == NULL) return -1; - if (perf_target__has_task(target)) + if (target__has_task(target)) evlist->cpus = cpu_map__dummy_new(); - else if (!perf_target__has_cpu(target) && !target->uses_mmap) + else if (!target__has_cpu(target) && !target->uses_mmap) evlist->cpus = cpu_map__dummy_new(); else evlist->cpus = cpu_map__new(target->cpu_list); @@ -1031,8 +1030,7 @@ out_err: return err; } -int perf_evlist__prepare_workload(struct perf_evlist *evlist, - struct perf_target *target, +int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, const char *argv[], bool pipe_output, bool want_signal) { @@ -1084,7 +1082,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, exit(-1); } - if (perf_target__none(target)) + if (target__none(target)) evlist->threads->map[0] = evlist->workload.pid; close(child_ready_pipe[1]); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index ecaa582f40e2..649d6ea98a84 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -102,7 +102,7 @@ void perf_evlist__config(struct perf_evlist *evlist, int perf_record_opts__config(struct perf_record_opts *opts); int perf_evlist__prepare_workload(struct perf_evlist *evlist, - struct perf_target *target, + struct target *target, const char *argv[], bool pipe_output, bool want_signal); int perf_evlist__start_workload(struct perf_evlist *evlist); @@ -134,8 +134,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, evlist->threads = threads; } -int perf_evlist__create_maps(struct perf_evlist *evlist, - struct perf_target *target); +int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target); void perf_evlist__delete_maps(struct perf_evlist *evlist); int perf_evlist__apply_filters(struct perf_evlist *evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f95653a639a6..18f7c188ff63 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -645,7 +645,7 @@ void perf_evsel__config(struct perf_evsel *evsel, } } - if (perf_target__has_cpu(&opts->target)) + if (target__has_cpu(&opts->target)) perf_evsel__set_sample_bit(evsel, CPU); if (opts->period) @@ -653,7 +653,7 @@ void perf_evsel__config(struct perf_evsel *evsel, if (!perf_missing_features.sample_id_all && (opts->sample_time || !opts->no_inherit || - perf_target__has_cpu(&opts->target))) + target__has_cpu(&opts->target))) perf_evsel__set_sample_bit(evsel, TIME); if (opts->raw_samples) { @@ -696,7 +696,7 @@ void perf_evsel__config(struct perf_evsel *evsel, * Setting enable_on_exec for independent events and * group leaders for traced executed by perf. */ - if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) + if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) attr->enable_on_exec = 1; } @@ -2006,8 +2006,7 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, return false; } -int perf_evsel__open_strerror(struct perf_evsel *evsel, - struct perf_target *target, +int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, int err, char *msg, size_t size) { switch (err) { diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 0178233abd64..f5029653dcd7 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -318,8 +318,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, bool perf_evsel__fallback(struct perf_evsel *evsel, int err, char *msg, size_t msgsize); -int perf_evsel__open_strerror(struct perf_evsel *evsel, - struct perf_target *target, +int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, int err, char *msg, size_t size); static inline int perf_evsel__group_idx(struct perf_evsel *evsel) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 680700b6d779..0393912d8033 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1396,12 +1396,12 @@ int machine__for_each_thread(struct machine *machine, } int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, - struct perf_target *target, struct thread_map *threads, + struct target *target, struct thread_map *threads, perf_event__handler_t process, bool data_mmap) { - if (perf_target__has_task(target)) + if (target__has_task(target)) return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap); - else if (perf_target__has_cpu(target)) + else if (target__has_cpu(target)) return perf_event__synthesize_threads(tool, process, machine, data_mmap); /* command specified */ return 0; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index fedd1dfaf715..477133015440 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -180,10 +180,10 @@ int machine__for_each_thread(struct machine *machine, void *priv); int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, - struct perf_target *target, struct thread_map *threads, + struct target *target, struct thread_map *threads, perf_event__handler_t process, bool data_mmap); static inline -int machine__synthesize_threads(struct machine *machine, struct perf_target *target, +int machine__synthesize_threads(struct machine *machine, struct target *target, struct thread_map *threads, bool data_mmap) { return __machine__synthesize_threads(machine, NULL, target, threads, diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 065528b7563e..3c778a07b7cc 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c @@ -13,9 +13,9 @@ #include -enum perf_target_errno perf_target__validate(struct perf_target *target) +enum target_errno target__validate(struct target *target) { - enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS; + enum target_errno ret = TARGET_ERRNO__SUCCESS; if (target->pid) target->tid = target->pid; @@ -23,42 +23,42 @@ enum perf_target_errno perf_target__validate(struct perf_target *target) /* CPU and PID are mutually exclusive */ if (target->tid && target->cpu_list) { target->cpu_list = NULL; - if (ret == PERF_ERRNO_TARGET__SUCCESS) - ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU; + if (ret == TARGET_ERRNO__SUCCESS) + ret = TARGET_ERRNO__PID_OVERRIDE_CPU; } /* UID and PID are mutually exclusive */ if (target->tid && target->uid_str) { target->uid_str = NULL; - if (ret == PERF_ERRNO_TARGET__SUCCESS) - ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID; + if (ret == TARGET_ERRNO__SUCCESS) + ret = TARGET_ERRNO__PID_OVERRIDE_UID; } /* UID and CPU are mutually exclusive */ if (target->uid_str && target->cpu_list) { target->cpu_list = NULL; - if (ret == PERF_ERRNO_TARGET__SUCCESS) - ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU; + if (ret == TARGET_ERRNO__SUCCESS) + ret = TARGET_ERRNO__UID_OVERRIDE_CPU; } /* PID and SYSTEM are mutually exclusive */ if (target->tid && target->system_wide) { target->system_wide = false; - if (ret == PERF_ERRNO_TARGET__SUCCESS) - ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM; + if (ret == TARGET_ERRNO__SUCCESS) + ret = TARGET_ERRNO__PID_OVERRIDE_SYSTEM; } /* UID and SYSTEM are mutually exclusive */ if (target->uid_str && target->system_wide) { target->system_wide = false; - if (ret == PERF_ERRNO_TARGET__SUCCESS) - ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM; + if (ret == TARGET_ERRNO__SUCCESS) + ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM; } return ret; } -enum perf_target_errno perf_target__parse_uid(struct perf_target *target) +enum target_errno target__parse_uid(struct target *target) { struct passwd pwd, *result; char buf[1024]; @@ -66,7 +66,7 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target) target->uid = UINT_MAX; if (str == NULL) - return PERF_ERRNO_TARGET__SUCCESS; + return TARGET_ERRNO__SUCCESS; /* Try user name first */ getpwnam_r(str, &pwd, buf, sizeof(buf), &result); @@ -79,22 +79,22 @@ enum perf_target_errno perf_target__parse_uid(struct perf_target *target) int uid = strtol(str, &endptr, 10); if (*endptr != '\0') - return PERF_ERRNO_TARGET__INVALID_UID; + return TARGET_ERRNO__INVALID_UID; getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); if (result == NULL) - return PERF_ERRNO_TARGET__USER_NOT_FOUND; + return TARGET_ERRNO__USER_NOT_FOUND; } target->uid = result->pw_uid; - return PERF_ERRNO_TARGET__SUCCESS; + return TARGET_ERRNO__SUCCESS; } /* - * This must have a same ordering as the enum perf_target_errno. + * This must have a same ordering as the enum target_errno. */ -static const char *perf_target__error_str[] = { +static const char *target__error_str[] = { "PID/TID switch overriding CPU", "PID/TID switch overriding UID", "UID switch overriding CPU", @@ -104,7 +104,7 @@ static const char *perf_target__error_str[] = { "Problems obtaining information for user %s", }; -int perf_target__strerror(struct perf_target *target, int errnum, +int target__strerror(struct target *target, int errnum, char *buf, size_t buflen) { int idx; @@ -124,21 +124,19 @@ int perf_target__strerror(struct perf_target *target, int errnum, return 0; } - if (errnum < __PERF_ERRNO_TARGET__START || - errnum >= __PERF_ERRNO_TARGET__END) + if (errnum < __TARGET_ERRNO__START || errnum >= __TARGET_ERRNO__END) return -1; - idx = errnum - __PERF_ERRNO_TARGET__START; - msg = perf_target__error_str[idx]; + idx = errnum - __TARGET_ERRNO__START; + msg = target__error_str[idx]; switch (errnum) { - case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU - ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM: + case TARGET_ERRNO__PID_OVERRIDE_CPU ... TARGET_ERRNO__UID_OVERRIDE_SYSTEM: snprintf(buf, buflen, "%s", msg); break; - case PERF_ERRNO_TARGET__INVALID_UID: - case PERF_ERRNO_TARGET__USER_NOT_FOUND: + case TARGET_ERRNO__INVALID_UID: + case TARGET_ERRNO__USER_NOT_FOUND: snprintf(buf, buflen, msg, target->uid_str); break; diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index a4be8575fda5..89bab7129de4 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h @@ -4,7 +4,7 @@ #include #include -struct perf_target { +struct target { const char *pid; const char *tid; const char *cpu_list; @@ -14,8 +14,8 @@ struct perf_target { bool uses_mmap; }; -enum perf_target_errno { - PERF_ERRNO_TARGET__SUCCESS = 0, +enum target_errno { + TARGET_ERRNO__SUCCESS = 0, /* * Choose an arbitrary negative big number not to clash with standard @@ -24,42 +24,40 @@ enum perf_target_errno { * * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html */ - __PERF_ERRNO_TARGET__START = -10000, + __TARGET_ERRNO__START = -10000, + /* for target__validate() */ + TARGET_ERRNO__PID_OVERRIDE_CPU = __TARGET_ERRNO__START, + TARGET_ERRNO__PID_OVERRIDE_UID, + TARGET_ERRNO__UID_OVERRIDE_CPU, + TARGET_ERRNO__PID_OVERRIDE_SYSTEM, + TARGET_ERRNO__UID_OVERRIDE_SYSTEM, - /* for perf_target__validate() */ - PERF_ERRNO_TARGET__PID_OVERRIDE_CPU = __PERF_ERRNO_TARGET__START, - PERF_ERRNO_TARGET__PID_OVERRIDE_UID, - PERF_ERRNO_TARGET__UID_OVERRIDE_CPU, - PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM, - PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM, + /* for target__parse_uid() */ + TARGET_ERRNO__INVALID_UID, + TARGET_ERRNO__USER_NOT_FOUND, - /* for perf_target__parse_uid() */ - PERF_ERRNO_TARGET__INVALID_UID, - PERF_ERRNO_TARGET__USER_NOT_FOUND, - - __PERF_ERRNO_TARGET__END, + __TARGET_ERRNO__END, }; -enum perf_target_errno perf_target__validate(struct perf_target *target); -enum perf_target_errno perf_target__parse_uid(struct perf_target *target); +enum target_errno target__validate(struct target *target); +enum target_errno target__parse_uid(struct target *target); -int perf_target__strerror(struct perf_target *target, int errnum, char *buf, - size_t buflen); +int target__strerror(struct target *target, int errnum, char *buf, size_t buflen); -static inline bool perf_target__has_task(struct perf_target *target) +static inline bool target__has_task(struct target *target) { return target->tid || target->pid || target->uid_str; } -static inline bool perf_target__has_cpu(struct perf_target *target) +static inline bool target__has_cpu(struct target *target) { return target->system_wide || target->cpu_list; } -static inline bool perf_target__none(struct perf_target *target) +static inline bool target__none(struct target *target) { - return !perf_target__has_task(target) && !perf_target__has_cpu(target); + return !target__has_task(target) && !target__has_cpu(target); } #endif /* _PERF_TARGET_H */ diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index f857b51b6bde..ce793c7dd23c 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -27,7 +27,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) float ksamples_per_sec; float esamples_percent; struct perf_record_opts *opts = &top->record_opts; - struct perf_target *target = &opts->target; + struct target *target = &opts->target; size_t ret = 0; if (top->samples) { -- cgit v1.2.2 From b222213936ef7d48908be2fab7639dd535c88045 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Tue, 12 Nov 2013 22:24:24 -0800 Subject: perf tools: Remove trivial extra semincolon Accidentally ran into these, get rid of them. Signed-off-by: Davidlohr Bueso Link: http://lkml.kernel.org/r/1384323864.2527.8.camel@buesod1.americas.hpqcorp.net Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index dc6fa3fbb180..5ce2ace2d6c1 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1148,7 +1148,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) perf_evsel__name(evsel)); } - return printed + fprintf(fp, "\n");; + return printed + fprintf(fp, "\n"); } int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, -- cgit v1.2.2 From 9d4ecc8893832337daf241236841db966fa53489 Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Wed, 13 Nov 2013 15:32:06 -0300 Subject: perf tools: Synthesize anon MMAP records again When introducing the PERF_RECORD_MMAP2 in: 5c5e854bc760 perf tools: Add attr->mmap2 support A check for the number of entries parsed by sscanf was introduced that assumed all of the 8 fields needed to be correctly parsed so that particular /proc/pid/maps line would be considered synthesizable. That broke anon records synthesizing, as it doesn't have the 'execname' field. Fix it by keeping the sscanf return check, changing it to not require that the 'execname' variable be parsed, so that the preexisting logic can kick in and set it to '//anon'. This should get things like JIT profiling working again. Signed-off-by: Don Zickus Cc: Bill Gray Cc: Jiri Olsa Cc: Joe Mario Cc: Richard Fowles Cc: Stephane Eranian Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/n/tip-bo4akalno7579shpz29u867j@git.kernel.org [ commit log message is mine, dzickus reported the problem with a patch ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 6e3a846aed0e..bb788c109fe6 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -209,8 +209,10 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, &event->mmap.start, &event->mmap.len, prot, &event->mmap.pgoff, execname); - - if (n != 5) + /* + * Anon maps don't have the execname. + */ + if (n < 4) continue; /* * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c -- cgit v1.2.2 From d87fcb4a2d990ba2de9284ede84a816c5066d54b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 13 Nov 2013 15:56:40 -0300 Subject: perf evsel: Introduce perf_evsel__prev() method Just one use so far, on the hists browser, for completeness since there we use perf_evlist__{first,last} and perf_evsel__next() for handling the TAB and UNTAB keys. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-d09l4lejp5427enuf3igpckw@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index f5029653dcd7..1ea7c92e6e33 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -279,6 +279,11 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) return list_entry(evsel->node.next, struct perf_evsel, node); } +static inline struct perf_evsel *perf_evsel__prev(struct perf_evsel *evsel) +{ + return list_entry(evsel->node.prev, struct perf_evsel, node); +} + /** * perf_evsel__is_group_leader - Return whether given evsel is a leader event * -- cgit v1.2.2 From 37676af15c8d5a9689c9d1220d2a27d510cbe238 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 13 Nov 2013 17:40:36 -0300 Subject: perf symbols: Limit max callchain using max_stack on DWARF unwinding too It was affecting only frame-pointer (fp) based callchain processing. Usage example: perf top --call-graph dwarf,1024 --max-stack 2 Works for any tool that does callchain resolving and provides a --max-stack option. Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Waiman Long Link: http://lkml.kernel.org/n/tip-eu45v8s3tq9ruay8tpfyon79@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 2 +- tools/perf/util/unwind.c | 9 +++++---- tools/perf/util/unwind.h | 5 +++-- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 0393912d8033..84cdb072ac83 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1368,7 +1368,7 @@ int machine__resolve_callchain(struct machine *machine, return unwind__get_entries(unwind_entry, &callchain_cursor, machine, thread, evsel->attr.sample_regs_user, - sample); + sample, max_stack); } diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c index 5390d0b8862a..0efd5393de85 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind.c @@ -559,7 +559,7 @@ static unw_accessors_t accessors = { }; static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, - void *arg) + void *arg, int max_stack) { unw_addr_space_t addr_space; unw_cursor_t c; @@ -575,7 +575,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, if (ret) display_error(ret); - while (!ret && (unw_step(&c) > 0)) { + while (!ret && (unw_step(&c) > 0) && max_stack--) { unw_word_t ip; unw_get_reg(&c, UNW_REG_IP, &ip); @@ -588,7 +588,8 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct machine *machine, struct thread *thread, - u64 sample_uregs, struct perf_sample *data) + u64 sample_uregs, struct perf_sample *data, + int max_stack) { unw_word_t ip; struct unwind_info ui = { @@ -610,5 +611,5 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, if (ret) return -ENOMEM; - return get_entries(&ui, cb, arg); + return get_entries(&ui, cb, arg, max_stack); } diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index ec0c71a2ca2e..d5966f49e22c 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -18,7 +18,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct machine *machine, struct thread *thread, u64 sample_uregs, - struct perf_sample *data); + struct perf_sample *data, int max_stack); int unwind__arch_reg_id(int regnum); #else static inline int @@ -27,7 +27,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, struct machine *machine __maybe_unused, struct thread *thread __maybe_unused, u64 sample_uregs __maybe_unused, - struct perf_sample *data __maybe_unused) + struct perf_sample *data __maybe_unused, + int max_stack __maybe_unused) { return 0; } -- cgit v1.2.2 From 539e6bb71e350541105e67e3d6c31392d9da25ef Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 1 Nov 2013 15:51:34 +0200 Subject: perf record: Add an option to force per-cpu mmaps By default, when tasks are specified (i.e. -p, -t or -u options) per-thread mmaps are created. Add an option to override that and force per-cpu mmaps. Further comments by peterz: So this option allows -t/-p/-u to create one buffer per cpu and attach all the various thread/process/user tasks' their counters to that one buffer? As opposed to the current state where each such counter would have its own buffer. Signed-off-by: Adrian Hunter Tested-by: Sukadev Bhattiprolu Acked-by: Peter Zijlstra Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1383313899-15987-7-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 4 +++- tools/perf/util/evsel.c | 4 ++-- tools/perf/util/target.h | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 5ce2ace2d6c1..bbc746aa5716 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -819,7 +819,9 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) if (evlist->threads == NULL) return -1; - if (target__has_task(target)) + if (target->force_per_cpu) + evlist->cpus = cpu_map__new(target->cpu_list); + else if (target__has_task(target)) evlist->cpus = cpu_map__dummy_new(); else if (!target__has_cpu(target) && !target->uses_mmap) evlist->cpus = cpu_map__dummy_new(); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 18f7c188ff63..46dd4c2a41ce 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -645,7 +645,7 @@ void perf_evsel__config(struct perf_evsel *evsel, } } - if (target__has_cpu(&opts->target)) + if (target__has_cpu(&opts->target) || opts->target.force_per_cpu) perf_evsel__set_sample_bit(evsel, CPU); if (opts->period) @@ -653,7 +653,7 @@ void perf_evsel__config(struct perf_evsel *evsel, if (!perf_missing_features.sample_id_all && (opts->sample_time || !opts->no_inherit || - target__has_cpu(&opts->target))) + target__has_cpu(&opts->target) || opts->target.force_per_cpu)) perf_evsel__set_sample_bit(evsel, TIME); if (opts->raw_samples) { diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index 89bab7129de4..2d0c50690892 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h @@ -12,6 +12,7 @@ struct target { uid_t uid; bool system_wide; bool uses_mmap; + bool force_per_cpu; }; enum target_errno { -- cgit v1.2.2