diff options
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/Build | 11 | ||||
-rw-r--r-- | tools/perf/util/bitmap.c | 31 | ||||
-rw-r--r-- | tools/perf/util/callchain.c | 28 | ||||
-rw-r--r-- | tools/perf/util/callchain.h | 1 | ||||
-rw-r--r-- | tools/perf/util/cpumap.c | 13 | ||||
-rw-r--r-- | tools/perf/util/evlist.c | 72 | ||||
-rw-r--r-- | tools/perf/util/evlist.h | 7 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 23 | ||||
-rw-r--r-- | tools/perf/util/evsel.h | 1 | ||||
-rwxr-xr-x | tools/perf/util/generate-cmdlist.sh | 15 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 78 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 24 | ||||
-rw-r--r-- | tools/perf/util/include/linux/bitmap.h | 66 | ||||
-rw-r--r-- | tools/perf/util/pmu.c | 1 | ||||
-rw-r--r-- | tools/perf/util/python-ext-sources | 2 | ||||
-rw-r--r-- | tools/perf/util/scripting-engines/trace-event-python.c | 115 | ||||
-rw-r--r-- | tools/perf/util/sort.c | 601 | ||||
-rw-r--r-- | tools/perf/util/sort.h | 12 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 1 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 3 | ||||
-rw-r--r-- | tools/perf/util/trace-event.h | 4 | ||||
-rw-r--r-- | tools/perf/util/unwind-libdw.c | 10 | ||||
-rw-r--r-- | tools/perf/util/unwind-libunwind.c | 20 | ||||
-rw-r--r-- | tools/perf/util/util.c | 27 | ||||
-rw-r--r-- | tools/perf/util/util.h | 2 |
25 files changed, 965 insertions, 203 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 94b1099f2c22..5eec53a3f4ac 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -9,7 +9,7 @@ libperf-y += env.o libperf-y += event.o libperf-y += evlist.o libperf-y += evsel.o -libperf-y += find_next_bit.o +libperf-y += find_bit.o libperf-y += kallsyms.o libperf-y += levenshtein.o libperf-y += llvm-utils.o @@ -132,7 +132,8 @@ CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c -CFLAGS_find_next_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" +CFLAGS_bitmap.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" +CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" @@ -142,7 +143,11 @@ $(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) -$(OUTPUT)util/find_next_bit.o: ../lib/util/find_next_bit.c FORCE +$(OUTPUT)util/bitmap.o: ../lib/bitmap.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) + +$(OUTPUT)util/find_bit.o: ../lib/find_bit.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c deleted file mode 100644 index 0a1adc1111fd..000000000000 --- a/tools/perf/util/bitmap.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * From lib/bitmap.c - * Helper functions for bitmap.h. - * - * This source code is licensed under the GNU General Public License, - * Version 2. See the file COPYING for more details. - */ -#include <linux/bitmap.h> - -int __bitmap_weight(const unsigned long *bitmap, int bits) -{ - int k, w = 0, lim = bits/BITS_PER_LONG; - - for (k = 0; k < lim; k++) - w += hweight_long(bitmap[k]); - - if (bits % BITS_PER_LONG) - w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits)); - - return w; -} - -void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits) -{ - int k; - int nr = BITS_TO_LONGS(bits); - - for (k = 0; k < nr; k++) - dst[k] = bitmap1[k] | bitmap2[k]; -} diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 564377d2bebf..53c43eb9489e 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -925,6 +925,34 @@ void free_callchain(struct callchain_root *root) free_callchain_node(&root->node); } +static u64 decay_callchain_node(struct callchain_node *node) +{ + struct callchain_node *child; + struct rb_node *n; + u64 child_hits = 0; + + n = rb_first(&node->rb_root_in); + while (n) { + child = container_of(n, struct callchain_node, rb_node_in); + + child_hits += decay_callchain_node(child); + n = rb_next(n); + } + + node->hit = (node->hit * 7) / 8; + node->children_hit = child_hits; + + return node->hit; +} + +void decay_callchain(struct callchain_root *root) +{ + if (!symbol_conf.use_callchain) + return; + + decay_callchain_node(&root->node); +} + int callchain_node__make_parent_list(struct callchain_node *node) { struct callchain_node *parent = node->parent; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 8ac8f043004c..18dd22269764 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -253,6 +253,7 @@ int callchain_node__fprintf_value(struct callchain_node *node, FILE *fp, u64 total); void free_callchain(struct callchain_root *root); +void decay_callchain(struct callchain_root *root); int callchain_node__make_parent_list(struct callchain_node *node); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index a0717b93d8f5..fa935093a599 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -188,8 +188,17 @@ static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus) if (map) { unsigned i; - for (i = 0; i < cpus->nr; i++) - map->map[i] = (int)cpus->cpu[i]; + for (i = 0; i < cpus->nr; i++) { + /* + * Special treatment for -1, which is not real cpu number, + * and we need to use (int) -1 to initialize map[i], + * otherwise it would become 65535. + */ + if (cpus->cpu[i] == (u16) -1) + map->map[i] = -1; + else + map->map[i] = (int) cpus->cpu[i]; + } } return map; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b9eac0daa0b9..d81f13de2476 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -68,6 +68,18 @@ struct perf_evlist *perf_evlist__new_default(void) return evlist; } +struct perf_evlist *perf_evlist__new_dummy(void) +{ + struct perf_evlist *evlist = perf_evlist__new(); + + if (evlist && perf_evlist__add_dummy(evlist)) { + perf_evlist__delete(evlist); + evlist = NULL; + } + + return evlist; +} + /** * perf_evlist__set_id_pos - set the positions of event ids. * @evlist: selected event list @@ -248,6 +260,22 @@ error: return -ENOMEM; } +int perf_evlist__add_dummy(struct perf_evlist *evlist) +{ + struct perf_event_attr attr = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_DUMMY, + .size = sizeof(attr), /* to capture ABI version */ + }; + struct perf_evsel *evsel = perf_evsel__new(&attr); + + if (evsel == NULL) + return -ENOMEM; + + perf_evlist__add(evlist, evsel); + return 0; +} + static int perf_evlist__add_attrs(struct perf_evlist *evlist, struct perf_event_attr *attrs, size_t nr_attrs) { @@ -365,48 +393,6 @@ void perf_evlist__toggle_enable(struct perf_evlist *evlist) (evlist->enabled ? perf_evlist__disable : perf_evlist__enable)(evlist); } -int perf_evlist__disable_event(struct perf_evlist *evlist, - struct perf_evsel *evsel) -{ - int cpu, thread, err; - int nr_cpus = cpu_map__nr(evlist->cpus); - int nr_threads = perf_evlist__nr_threads(evlist, evsel); - - if (!evsel->fd) - return 0; - - for (cpu = 0; cpu < nr_cpus; cpu++) { - for (thread = 0; thread < nr_threads; thread++) { - err = ioctl(FD(evsel, cpu, thread), - PERF_EVENT_IOC_DISABLE, 0); - if (err) - return err; - } - } - return 0; -} - -int perf_evlist__enable_event(struct perf_evlist *evlist, - struct perf_evsel *evsel) -{ - int cpu, thread, err; - int nr_cpus = cpu_map__nr(evlist->cpus); - int nr_threads = perf_evlist__nr_threads(evlist, evsel); - - if (!evsel->fd) - return -EINVAL; - - for (cpu = 0; cpu < nr_cpus; cpu++) { - for (thread = 0; thread < nr_threads; thread++) { - err = ioctl(FD(evsel, cpu, thread), - PERF_EVENT_IOC_ENABLE, 0); - if (err) - return err; - } - } - return 0; -} - static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, struct perf_evsel *evsel, int cpu) { @@ -1470,7 +1456,7 @@ int perf_evlist__open(struct perf_evlist *evlist) perf_evlist__update_id_pos(evlist); evlist__for_each(evlist, evsel) { - err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); + err = perf_evsel__open(evsel, evsel->cpus, evsel->threads); if (err < 0) goto out_err; } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 139a50038097..7c4d9a206776 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -67,6 +67,7 @@ struct perf_evsel_str_handler { struct perf_evlist *perf_evlist__new(void); struct perf_evlist *perf_evlist__new_default(void); +struct perf_evlist *perf_evlist__new_dummy(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); @@ -81,6 +82,8 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, #define perf_evlist__add_default_attrs(evlist, array) \ __perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array)) +int perf_evlist__add_dummy(struct perf_evlist *evlist); + int perf_evlist__add_newtp(struct perf_evlist *evlist, const char *sys, const char *name, void *handler); @@ -152,10 +155,6 @@ void perf_evlist__disable(struct perf_evlist *evlist); void perf_evlist__enable(struct perf_evlist *evlist); void perf_evlist__toggle_enable(struct perf_evlist *evlist); -int perf_evlist__disable_event(struct perf_evlist *evlist, - struct perf_evsel *evsel); -int perf_evlist__enable_event(struct perf_evlist *evlist, - struct perf_evsel *evsel); int perf_evlist__enable_event_idx(struct perf_evlist *evlist, struct perf_evsel *evsel, int idx); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 544e4400de13..cdbaf9b51e42 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2298,6 +2298,29 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, printed += comma_fprintf(fp, &first, " %s=%" PRIu64, term, (u64)evsel->attr.sample_freq); } + + if (details->trace_fields) { + struct format_field *field; + + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { + printed += comma_fprintf(fp, &first, " (not a tracepoint)"); + goto out; + } + + field = evsel->tp_format->format.fields; + if (field == NULL) { + printed += comma_fprintf(fp, &first, " (no trace field)"); + goto out; + } + + printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name); + + field = field->next; + while (field) { + printed += comma_fprintf(fp, &first, "%s", field->name); + field = field->next; + } + } out: fputc('\n', fp); return ++printed; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 5ded1fc0341e..8e75434bd01c 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -369,6 +369,7 @@ struct perf_attr_details { bool verbose; bool event_group; bool force; + bool trace_fields; }; int perf_evsel__fprintf(struct perf_evsel *evsel, diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index 36a885d2cd22..0ac2037c970c 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh @@ -36,4 +36,19 @@ do }' "Documentation/perf-$cmd.txt" done echo "#endif /* HAVE_LIBELF_SUPPORT */" + +echo "#ifdef HAVE_LIBAUDIT_SUPPORT" +sed -n -e 's/^perf-\([^ ]*\)[ ].* audit*/\1/p' command-list.txt | +sort | +while read cmd +do + sed -n ' + /^NAME/,/perf-'"$cmd"'/H + ${ + x + s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/ + p + }' "Documentation/perf-$cmd.txt" +done +echo "#endif /* HAVE_LIBELF_SUPPORT */" echo "};" diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 56e97f5af598..c226303e3da0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -254,6 +254,7 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) he_stat__decay(&he->stat); if (symbol_conf.cumulate_callchain) he_stat__decay(he->stat_acc); + decay_callchain(he->callchain); diff = prev_period - he->stat.period; @@ -369,6 +370,25 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, if (symbol_conf.use_callchain) callchain_init(he->callchain); + if (he->raw_data) { + he->raw_data = memdup(he->raw_data, he->raw_size); + + if (he->raw_data == NULL) { + map__put(he->ms.map); + if (he->branch_info) { + map__put(he->branch_info->from.map); + map__put(he->branch_info->to.map); + free(he->branch_info); + } + if (he->mem_info) { + map__put(he->mem_info->iaddr.map); + map__put(he->mem_info->daddr.map); + } + free(he->stat_acc); + free(he); + return NULL; + } + } INIT_LIST_HEAD(&he->pairs.node); thread__get(he->thread); } @@ -461,7 +481,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, struct symbol *sym_parent, struct branch_info *bi, struct mem_info *mi, - u64 period, u64 weight, u64 transaction, + struct perf_sample *sample, bool sample_self) { struct hist_entry entry = { @@ -478,15 +498,17 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .level = al->level, .stat = { .nr_events = 1, - .period = period, - .weight = weight, + .period = sample->period, + .weight = sample->weight, }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent) | al->filtered, .hists = hists, .branch_info = bi, .mem_info = mi, - .transaction = transaction, + .transaction = sample->transaction, + .raw_data = sample->raw_data, + .raw_size = sample->raw_size, }; return hists__findnew_entry(hists, &entry, al, sample_self); @@ -526,12 +548,13 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al u64 cost; struct mem_info *mi = iter->priv; struct hists *hists = evsel__hists(iter->evsel); + struct perf_sample *sample = iter->sample; struct hist_entry *he; if (mi == NULL) return -EINVAL; - cost = iter->sample->weight; + cost = sample->weight; if (!cost) cost = 1; @@ -542,8 +565,10 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al * and this is indirectly achieved by passing period=weight here * and the he_stat__add_period() function. */ + sample->period = cost; + he = __hists__add_entry(hists, al, iter->parent, NULL, mi, - cost, cost, 0, true); + sample, true); if (!he) return -ENOMEM; @@ -630,6 +655,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a struct branch_info *bi; struct perf_evsel *evsel = iter->evsel; struct hists *hists = evsel__hists(evsel); + struct perf_sample *sample = iter->sample; struct hist_entry *he = NULL; int i = iter->curr; int err = 0; @@ -643,9 +669,11 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a * The report shows the percentage of total branches captured * and not events sampled. Thus we use a pseudo period of 1. */ + sample->period = 1; + sample->weight = bi->flags.cycles ? bi->flags.cycles : 1; + he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL, - 1, bi->flags.cycles ? bi->flags.cycles : 1, - 0, true); + sample, true); if (he == NULL) return -ENOMEM; @@ -682,8 +710,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location struct hist_entry *he; he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, - sample->period, sample->weight, - sample->transaction, true); + sample, true); if (he == NULL) return -ENOMEM; @@ -744,8 +771,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, int err = 0; he = __hists__add_entry(hists, al, iter->parent, NULL, NULL, - sample->period, sample->weight, - sample->transaction, true); + sample, true); if (he == NULL) return -ENOMEM; @@ -797,6 +823,8 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, .sym = al->sym, }, .parent = iter->parent, + .raw_data = sample->raw_data, + .raw_size = sample->raw_size, }; int i; struct callchain_cursor cursor; @@ -818,8 +846,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, } he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, - sample->period, sample->weight, - sample->transaction, false); + sample, false); if (he == NULL) return -ENOMEM; @@ -971,6 +998,8 @@ void hist_entry__delete(struct hist_entry *he) if (he->srcfile && he->srcfile[0]) free(he->srcfile); free_callchain(he->callchain); + free(he->trace_output); + free(he->raw_data); free(he); } @@ -978,9 +1007,8 @@ void hist_entry__delete(struct hist_entry *he) * collapse the histogram */ -static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, - struct rb_root *root, - struct hist_entry *he) +bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, + struct rb_root *root, struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -1020,7 +1048,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, return true; } -static struct rb_root *hists__get_rotate_entries_in(struct hists *hists) +struct rb_root *hists__get_rotate_entries_in(struct hists *hists) { struct rb_root *root; @@ -1084,7 +1112,7 @@ static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) int64_t cmp = 0; perf_hpp__for_each_sort_list(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, a->hists)) continue; cmp = fmt->sort(fmt, a, b); @@ -1555,10 +1583,8 @@ int perf_hist_config(const char *var, const char *value) return 0; } -static int hists_evsel__init(struct perf_evsel *evsel) +int __hists__init(struct hists *hists) { - struct hists *hists = evsel__hists(evsel); - memset(hists, 0, sizeof(*hists)); hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; hists->entries_in = &hists->entries_in_array[0]; @@ -1598,6 +1624,14 @@ static void hists_evsel__exit(struct perf_evsel *evsel) hists__delete_all_entries(hists); } +static int hists_evsel__init(struct perf_evsel *evsel) +{ + struct hists *hists = evsel__hists(evsel); + + __hists__init(hists); + return 0; +} + /* * XXX We probably need a hists_evsel__exit() to free the hist_entries * stored in the rbtree... diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index a48a2078d288..d4ec4822a103 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -52,6 +52,7 @@ enum hist_column { HISTC_MEM_IADDR_SYMBOL, HISTC_TRANSACTION, HISTC_CYCLES, + HISTC_TRACE, HISTC_NR_COLS, /* Last entry */ }; @@ -114,8 +115,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *parent, struct branch_info *bi, - struct mem_info *mi, u64 period, - u64 weight, u64 transaction, + struct mem_info *mi, + struct perf_sample *sample, bool sample_self); int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, int max_stack_depth, void *arg); @@ -184,6 +185,11 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel) } int hists__init(void); +int __hists__init(struct hists *hists); + +struct rb_root *hists__get_rotate_entries_in(struct hists *hists); +bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, + struct rb_root *root, struct hist_entry *he); struct perf_hpp { char *buf; @@ -261,10 +267,20 @@ void perf_hpp__append_sort_keys(void); bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); +bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *format); +bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists); -static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) +static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format, + struct hists *hists) { - return format->elide; + if (format->elide) + return true; + + if (perf_hpp__is_dynamic_entry(format) && + !perf_hpp__defined_dynamic_entry(format, hists)) + return true; + + return false; } void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h deleted file mode 100644 index 40bd21488032..000000000000 --- a/tools/perf/util/include/linux/bitmap.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef _PERF_BITOPS_H -#define _PERF_BITOPS_H - -#include <string.h> -#include <linux/bitops.h> - -#define DECLARE_BITMAP(name,bits) \ - unsigned long name[BITS_TO_LONGS(bits)] - -int __bitmap_weight(const unsigned long *bitmap, int bits); -void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits); - -#define BITMAP_LAST_WORD_MASK(nbits) \ -( \ - ((nbits) % BITS_PER_LONG) ? \ - (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \ -) - -#define small_const_nbits(nbits) \ - (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) - -static inline void bitmap_zero(unsigned long *dst, int nbits) -{ - if (small_const_nbits(nbits)) - *dst = 0UL; - else { - int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); - memset(dst, 0, len); - } -} - -static inline int bitmap_weight(const unsigned long *src, int nbits) -{ - if (small_const_nbits(nbits)) - return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits)); - return __bitmap_weight(src, nbits); -} - -static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, - const unsigned long *src2, int nbits) -{ - if (small_const_nbits(nbits)) - *dst = *src1 | *src2; - else - __bitmap_or(dst, src1, src2, nbits); -} - -/** - * test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set - * @addr: Address to count from - */ -static inline int test_and_set_bit(int nr, unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - unsigned long old; - - old = *p; - *p = old | mask; - - return (old & mask) != 0; -} - -#endif /* _PERF_BITOPS_H */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index e4b173dec4b9..b597bcc8fc78 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -220,6 +220,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, alias->scale = 1.0; alias->unit[0] = '\0'; alias->per_pkg = false; + alias->snapshot = false; ret = parse_events_terms(&alias->terms, val); if (ret) { diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 51be28b1bca2..8162ba0e2e57 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -10,6 +10,8 @@ util/ctype.c util/evlist.c util/evsel.c util/cpumap.c +../lib/bitmap.c +../lib/find_bit.c ../lib/hweight.c util/thread_map.c util/util.c diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index a8e825fca42a..d72fafc1c800 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -41,6 +41,9 @@ #include "../thread-stack.h" #include "../trace-event.h" #include "../machine.h" +#include "thread_map.h" +#include "cpumap.h" +#include "stat.h" PyMODINIT_FUNC initperf_trace_context(void); @@ -859,6 +862,104 @@ static void python_process_event(union perf_event *event, } } +static void get_handler_name(char *str, size_t size, + struct perf_evsel *evsel) +{ + char *p = str; + + scnprintf(str, size, "stat__%s", perf_evsel__name(evsel)); + + while ((p = strchr(p, ':'))) { + *p = '_'; + p++; + } +} + +static void +process_stat(struct perf_evsel *counter, int cpu, int thread, u64 tstamp, + struct perf_counts_values *count) +{ + PyObject *handler, *t; + static char handler_name[256]; + int n = 0; + + t = PyTuple_New(MAX_FIELDS); + if (!t) + Py_FatalError("couldn't create Python tuple"); + + get_handler_name(handler_name, sizeof(handler_name), + counter); + + handler = get_handler(handler_name); + if (!handler) { + pr_debug("can't find python handler %s\n", handler_name); + return; + } + + PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); + PyTuple_SetItem(t, n++, PyInt_FromLong(thread)); + + tuple_set_u64(t, n++, tstamp); + tuple_set_u64(t, n++, count->val); + tuple_set_u64(t, n++, count->ena); + tuple_set_u64(t, n++, count->run); + + if (_PyTuple_Resize(&t, n) == -1) + Py_FatalError("error resizing Python tuple"); + + call_object(handler, t, handler_name); + + Py_DECREF(t); +} + +static void python_process_stat(struct perf_stat_config *config, + struct perf_evsel *counter, u64 tstamp) +{ + struct thread_map *threads = counter->threads; + struct cpu_map *cpus = counter->cpus; + int cpu, thread; + + if (config->aggr_mode == AGGR_GLOBAL) { + process_stat(counter, -1, -1, tstamp, + &counter->counts->aggr); + return; + } + + for (thread = 0; thread < threads->nr; thread++) { + for (cpu = 0; cpu < cpus->nr; cpu++) { + process_stat(counter, cpus->map[cpu], + thread_map__pid(threads, thread), tstamp, + perf_counts(counter->counts, cpu, thread)); + } + } +} + +static void python_process_stat_interval(u64 tstamp) +{ + PyObject *handler, *t; + static const char handler_name[] = "stat__interval"; + int n = 0; + + t = PyTuple_New(MAX_FIELDS); + if (!t) + Py_FatalError("couldn't create Python tuple"); + + handler = get_handler(handler_name); + if (!handler) { + pr_debug("can't find python handler %s\n", handler_name); + return; + } + + tuple_set_u64(t, n++, tstamp); + + if (_PyTuple_Resize(&t, n) == -1) + Py_FatalError("error resizing Python tuple"); + + call_object(handler, t, handler_name); + + Py_DECREF(t); +} + static int run_start_sub(void) { main_module = PyImport_AddModule("__main__"); @@ -1201,10 +1302,12 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) } struct scripting_ops python_scripting_ops = { - .name = "Python", - .start_script = python_start_script, - .flush_script = python_flush_script, - .stop_script = python_stop_script, - .process_event = python_process_event, - .generate_script = python_generate_script, + .name = "Python", + .start_script = python_start_script, + .flush_script = python_flush_script, + .stop_script = python_stop_script, + .process_event = python_process_event, + .process_stat = python_process_stat, + .process_stat_interval = python_process_stat_interval, + .generate_script = python_generate_script, }; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2d8ccd4d9e1b..ec722346e6ff 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -4,6 +4,8 @@ #include "comm.h" #include "symbol.h" #include "evsel.h" +#include "evlist.h" +#include <traceevent/event-parse.h> regex_t parent_regex; const char default_parent_pattern[] = "^sys_|^do_page_fault"; @@ -13,6 +15,7 @@ const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cy const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; const char default_top_sort_order[] = "dso,symbol"; const char default_diff_sort_order[] = "dso,symbol"; +const char default_tracepoint_sort_order[] = "trace"; const char *sort_order; const char *field_order; regex_t ignore_callees_regex; @@ -443,6 +446,70 @@ struct sort_entry sort_socket = { .se_width_idx = HISTC_SOCKET, }; +/* --sort trace */ + +static char *get_trace_output(struct hist_entry *he) +{ + struct trace_seq seq; + struct perf_evsel *evsel; + struct pevent_record rec = { + .data = he->raw_data, + .size = he->raw_size, + }; + + evsel = hists_to_evsel(he->hists); + + trace_seq_init(&seq); + if (symbol_conf.raw_trace) { + pevent_print_fields(&seq, he->raw_data, he->raw_size, + evsel->tp_format); + } else { + pevent_event_info(&seq, evsel->tp_format, &rec); + } + return seq.buffer; +} + +static int64_t +sort__trace_cmp(struct hist_entry *left, struct hist_entry *right) +{ + struct perf_evsel *evsel; + + evsel = hists_to_evsel(left->hists); + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + return 0; + + if (left->trace_output == NULL) + left->trace_output = get_trace_output(left); + if (right->trace_output == NULL) + right->trace_output = get_trace_output(right); + + hists__new_col_len(left->hists, HISTC_TRACE, strlen(left->trace_output)); + hists__new_col_len(right->hists, HISTC_TRACE, strlen(right->trace_output)); + + return strcmp(right->trace_output, left->trace_output); +} + +static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf, + size_t size, unsigned int width) +{ + struct perf_evsel *evsel; + + evsel = hists_to_evsel(he->hists); + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + return scnprintf(bf, size, "%-*.*s", width, width, "N/A"); + + if (he->trace_output == NULL) + he->trace_output = get_trace_output(he); + return repsep_snprintf(bf, size, "%-*.*s", width, width, he->trace_output); +} + +struct sort_entry sort_trace = { + .se_header = "Trace output", + .se_cmp = sort__trace_cmp, + .se_snprintf = hist_entry__trace_snprintf, + .se_width_idx = HISTC_TRACE, +}; + /* sort keys for branch stacks */ static int64_t @@ -1312,6 +1379,7 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), DIM(SORT_TRANSACTION, "transaction", sort_transaction), + DIM(SORT_TRACE, "trace", sort_trace), }; #undef DIM @@ -1529,6 +1597,455 @@ static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) return 0; } +struct hpp_dynamic_entry { + struct perf_hpp_fmt hpp; + struct perf_evsel *evsel; + struct format_field *field; + unsigned dynamic_len; + bool raw_trace; +}; + +static int hde_width(struct hpp_dynamic_entry *hde) +{ + if (!hde->hpp.len) { + int len = hde->dynamic_len; + int namelen = strlen(hde->field->name); + int fieldlen = hde->field->size; + + if (namelen > len) + len = namelen; + + if (!(hde->field->flags & FIELD_IS_STRING)) { + /* length for print hex numbers */ + fieldlen = hde->field->size * 2 + 2; + } + if (fieldlen > len) + len = fieldlen; + + hde->hpp.len = len; + } + return hde->hpp.len; +} + +static void update_dynamic_len(struct hpp_dynamic_entry *hde, + struct hist_entry *he) +{ + char *str, *pos; + struct format_field *field = hde->field; + size_t namelen; + bool last = false; + + if (hde->raw_trace) + return; + + /* parse pretty print result and update max length */ + if (!he->trace_output) + he->trace_output = get_trace_output(he); + + namelen = strlen(field->name); + str = he->trace_output; + + while (str) { + pos = strchr(str, ' '); + if (pos == NULL) { + last = true; + pos = str + strlen(str); + } + + if (!strncmp(str, field->name, namelen)) { + size_t len; + + str += namelen + 1; + len = pos - str; + + if (len > hde->dynamic_len) + hde->dynamic_len = len; + break; + } + + if (last) + str = NULL; + else + str = pos + 1; + } +} + +static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct perf_evsel *evsel __maybe_unused) +{ + struct hpp_dynamic_entry *hde; + size_t len = fmt->user_len; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + if (!len) + len = hde_width(hde); + + return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name); +} + +static int __sort__hde_width(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp __maybe_unused, + struct perf_evsel *evsel __maybe_unused) +{ + struct hpp_dynamic_entry *hde; + size_t len = fmt->user_len; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + if (!len) + len = hde_width(hde); + + return len; +} + +bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists) +{ + struct hpp_dynamic_entry *hde; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + return hists_to_evsel(hists) == hde->evsel; +} + +static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he) +{ + struct hpp_dynamic_entry *hde; + size_t len = fmt->user_len; + char *str, *pos; + struct format_field *field; + size_t namelen; + bool last = false; + int ret; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + if (!len) + len = hde_width(hde); + + if (hde->raw_trace) + goto raw_field; + + field = hde->field; + namelen = strlen(field->name); + str = he->trace_output; + + while (str) { + pos = strchr(str, ' '); + if (pos == NULL) { + last = true; + pos = str + strlen(str); + } + + if (!strncmp(str, field->name, namelen)) { + str += namelen + 1; + str = strndup(str, pos - str); + + if (str == NULL) + return scnprintf(hpp->buf, hpp->size, + "%*.*s", len, len, "ERROR"); + break; + } + + if (last) + str = NULL; + else + str = pos + 1; + } + + if (str == NULL) { + struct trace_seq seq; +raw_field: + trace_seq_init(&seq); + pevent_print_field(&seq, he->raw_data, hde->field); + str = seq.buffer; + } + + ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str); + free(str); + return ret; +} + +static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, + struct hist_entry *a, struct hist_entry *b) +{ + struct hpp_dynamic_entry *hde; + struct format_field *field; + unsigned offset, size; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + field = hde->field; + if (field->flags & FIELD_IS_DYNAMIC) { + unsigned long long dyn; + + pevent_read_number_field(field, a->raw_data, &dyn); + offset = dyn & 0xffff; + size = (dyn >> 16) & 0xffff; + + /* record max width for output */ + if (size > hde->dynamic_len) + hde->dynamic_len = size; + } else { + offset = field->offset; + size = field->size; + + update_dynamic_len(hde, a); + update_dynamic_len(hde, b); + } + + return memcmp(a->raw_data + offset, b->raw_data + offset, size); +} + +bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt) +{ + return fmt->cmp == __sort__hde_cmp; +} + +static struct hpp_dynamic_entry * +__alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) +{ + struct hpp_dynamic_entry *hde; + + hde = malloc(sizeof(*hde)); + if (hde == NULL) { + pr_debug("Memory allocation failed\n"); + return NULL; + } + + hde->evsel = evsel; + hde->field = field; + hde->dynamic_len = 0; + + hde->hpp.name = field->name; + hde->hpp.header = __sort__hde_header; + hde->hpp.width = __sort__hde_width; + hde->hpp.entry = __sort__hde_entry; + hde->hpp.color = NULL; + + hde->hpp.cmp = __sort__hde_cmp; + hde->hpp.collapse = __sort__hde_cmp; + hde->hpp.sort = __sort__hde_cmp; + + INIT_LIST_HEAD(&hde->hpp.list); + INIT_LIST_HEAD(&hde->hpp.sort_list); + hde->hpp.elide = false; + hde->hpp.len = 0; + hde->hpp.user_len = 0; + + return hde; +} + +static int parse_field_name(char *str, char **event, char **field, char **opt) +{ + char *event_name, *field_name, *opt_name; + + event_name = str; + field_name = strchr(str, '.'); + + if (field_name) { + *field_name++ = '\0'; + } else { + event_name = NULL; + field_name = str; + } + + opt_name = strchr(field_name, '/'); + if (opt_name) + *opt_name++ = '\0'; + + *event = event_name; + *field = field_name; + *opt = opt_name; + + return 0; +} + +/* find match evsel using a given event name. The event name can be: + * 1. '%' + event index (e.g. '%1' for first event) + * 2. full event name (e.g. sched:sched_switch) + * 3. partial event name (should not contain ':') + */ +static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_name) +{ + struct perf_evsel *evsel = NULL; + struct perf_evsel *pos; + bool full_name; + + /* case 1 */ + if (event_name[0] == '%') { + int nr = strtol(event_name+1, NULL, 0); + + if (nr > evlist->nr_entries) + return NULL; + + evsel = perf_evlist__first(evlist); + while (--nr > 0) + evsel = perf_evsel__next(evsel); + + return evsel; + } + + full_name = !!strchr(event_name, ':'); + evlist__for_each(evlist, pos) { + /* case 2 */ + if (full_name && !strcmp(pos->name, event_name)) + return pos; + /* case 3 */ + if (!full_name && strstr(pos->name, event_name)) { + if (evsel) { + pr_debug("'%s' event is ambiguous: it can be %s or %s\n", + event_name, evsel->name, pos->name); + return NULL; + } + evsel = pos; + } + } + + return evsel; +} + +static int __dynamic_dimension__add(struct perf_evsel *evsel, + struct format_field *field, + bool raw_trace) +{ + struct hpp_dynamic_entry *hde; + + hde = __alloc_dynamic_entry(evsel, field); + if (hde == NULL) + return -ENOMEM; + + hde->raw_trace = raw_trace; + + perf_hpp__register_sort_field(&hde->hpp); + return 0; +} + +static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace) +{ + int ret; + struct format_field *field; + + field = evsel->tp_format->format.fields; + while (field) { + ret = __dynamic_dimension__add(evsel, field, raw_trace); + if (ret < 0) + return ret; + + field = field->next; + } + return 0; +} + +static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace) +{ + int ret; + struct perf_evsel *evsel; + + evlist__for_each(evlist, evsel) { + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + continue; + + ret = add_evsel_fields(evsel, raw_trace); + if (ret < 0) + return ret; + } + return 0; +} + +static int add_all_matching_fields(struct perf_evlist *evlist, + char *field_name, bool raw_trace) +{ + int ret = -ESRCH; + struct perf_evsel *evsel; + struct format_field *field; + + evlist__for_each(evlist, evsel) { + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + continue; + + field = pevent_find_any_field(evsel->tp_format, field_name); + if (field == NULL) + continue; + + ret = __dynamic_dimension__add(evsel, field, raw_trace); + if (ret < 0) + break; + } + return ret; +} + +static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) +{ + char *str, *event_name, *field_name, *opt_name; + struct perf_evsel *evsel; + struct format_field *field; + bool raw_trace = symbol_conf.raw_trace; + int ret = 0; + + if (evlist == NULL) + return -ENOENT; + + str = strdup(tok); + if (str == NULL) + return -ENOMEM; + + if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) { + ret = -EINVAL; + goto out; + } + + if (opt_name) { + if (strcmp(opt_name, "raw")) { + pr_debug("unsupported field option %s\n", opt_name); + ret = -EINVAL; + goto out; + } + raw_trace = true; + } + + if (!strcmp(field_name, "trace_fields")) { + ret = add_all_dynamic_fields(evlist, raw_trace); + goto out; + } + + if (event_name == NULL) { + ret = add_all_matching_fields(evlist, field_name, raw_trace); + goto out; + } + + evsel = find_evsel(evlist, event_name); + if (evsel == NULL) { + pr_debug("Cannot find event: %s\n", event_name); + ret = -ENOENT; + goto out; + } + + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { + pr_debug("%s is not a tracepoint event\n", event_name); + ret = -EINVAL; + goto out; + } + + if (!strcmp(field_name, "*")) { + ret = add_evsel_fields(evsel, raw_trace); + } else { + field = pevent_find_any_field(evsel->tp_format, field_name); + if (field == NULL) { + pr_debug("Cannot find event field for %s.%s\n", + event_name, field_name); + return -ENOENT; + } + + ret = __dynamic_dimension__add(evsel, field, raw_trace); + } + +out: + free(str); + return ret; +} + static int __sort_dimension__add(struct sort_dimension *sd) { if (sd->taken) @@ -1583,7 +2100,8 @@ int hpp_dimension__add_output(unsigned col) return __hpp_dimension__add_output(&hpp_sort_dimensions[col]); } -int sort_dimension__add(const char *tok) +static int sort_dimension__add(const char *tok, + struct perf_evlist *evlist __maybe_unused) { unsigned int i; @@ -1664,10 +2182,13 @@ int sort_dimension__add(const char *tok) return 0; } + if (!add_dynamic_entry(evlist, tok)) + return 0; + return -ESRCH; } -static const char *get_default_sort_order(void) +static const char *get_default_sort_order(struct perf_evlist *evlist) { const char *default_sort_orders[] = { default_sort_order, @@ -1675,14 +2196,33 @@ static const char *get_default_sort_order(void) default_mem_sort_order, default_top_sort_order, default_diff_sort_order, + default_tracepoint_sort_order, }; + bool use_trace = true; + struct perf_evsel *evsel; BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); + if (evlist == NULL) + goto out_no_evlist; + + evlist__for_each(evlist, evsel) { + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { + use_trace = false; + break; + } + } + + if (use_trace) { + sort__mode = SORT_MODE__TRACEPOINT; + if (symbol_conf.raw_trace) + return "trace_fields"; + } +out_no_evlist: return default_sort_orders[sort__mode]; } -static int setup_sort_order(void) +static int setup_sort_order(struct perf_evlist *evlist) { char *new_sort_order; @@ -1703,7 +2243,7 @@ static int setup_sort_order(void) * because it's checked over the rest of the code. */ if (asprintf(&new_sort_order, "%s,%s", - get_default_sort_order(), sort_order + 1) < 0) { + get_default_sort_order(evlist), sort_order + 1) < 0) { error("Not enough memory to set up --sort"); return -ENOMEM; } @@ -1712,13 +2252,41 @@ static int setup_sort_order(void) return 0; } -static int __setup_sorting(void) +/* + * Adds 'pre,' prefix into 'str' is 'pre' is + * not already part of 'str'. + */ +static char *prefix_if_not_in(const char *pre, char *str) +{ + char *n; + + if (!str || strstr(str, pre)) + return str; + + if (asprintf(&n, "%s,%s", pre, str) < 0) + return NULL; + + free(str); + return n; +} + +static char *setup_overhead(char *keys) +{ + keys = prefix_if_not_in("overhead", keys); + + if (symbol_conf.cumulate_callchain) + keys = prefix_if_not_in("overhead_children", keys); + + return keys; +} + +static int __setup_sorting(struct perf_evlist *evlist) { char *tmp, *tok, *str; const char *sort_keys; int ret = 0; - ret = setup_sort_order(); + ret = setup_sort_order(evlist); if (ret) return ret; @@ -1732,7 +2300,7 @@ static int __setup_sorting(void) return 0; } - sort_keys = get_default_sort_order(); + sort_keys = get_default_sort_order(evlist); } str = strdup(sort_keys); @@ -1741,9 +2309,20 @@ static int __setup_sorting(void) return -ENOMEM; } + /* + * Prepend overhead fields for backward compatibility. + */ + if (!is_strict_order(field_order)) { + str = setup_overhead(str); + if (str == NULL) { + error("Not enough memory to setup overhead keys"); + return -ENOMEM; + } + } + for (tok = strtok_r(str, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { - ret = sort_dimension__add(tok); + ret = sort_dimension__add(tok, evlist); if (ret == -EINVAL) { error("Invalid --sort key: `%s'", tok); break; @@ -1954,16 +2533,16 @@ out: return ret; } -int setup_sorting(void) +int setup_sorting(struct perf_evlist *evlist) { int err; - err = __setup_sorting(); + err = __setup_sorting(evlist); if (err < 0) return err; if (parent_pattern != default_parent_pattern) { - err = sort_dimension__add("parent"); + err = sort_dimension__add("parent", evlist); if (err < 0) return err; } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 86f05e7a5566..687bbb124428 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -122,6 +122,9 @@ struct hist_entry { struct branch_info *branch_info; struct hists *hists; struct mem_info *mem_info; + void *raw_data; + u32 raw_size; + void *trace_output; struct callchain_root callchain[0]; /* must be last member */ }; @@ -164,6 +167,7 @@ enum sort_mode { SORT_MODE__MEMORY, SORT_MODE__TOP, SORT_MODE__DIFF, + SORT_MODE__TRACEPOINT, }; enum sort_type { @@ -180,6 +184,7 @@ enum sort_type { SORT_LOCAL_WEIGHT, SORT_GLOBAL_WEIGHT, SORT_TRANSACTION, + SORT_TRACE, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, @@ -209,8 +214,6 @@ enum sort_type { */ struct sort_entry { - struct list_head list; - const char *se_header; int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); @@ -224,10 +227,11 @@ struct sort_entry { extern struct sort_entry sort_thread; extern struct list_head hist_entry__sort_list; -int setup_sorting(void); +struct perf_evlist; +struct pevent; +int setup_sorting(struct perf_evlist *evlist); int setup_output_field(void); void reset_output_field(void); -extern int sort_dimension__add(const char *); void sort__setup_elide(FILE *fp); void perf_hpp__set_elide(int idx, bool elide); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index d51abd2e7865..3b2de6eb3376 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -39,6 +39,7 @@ struct symbol_conf symbol_conf = { .cumulate_callchain = true, .show_hist_headers = true, .symfs = "", + .event_group = true, }; static enum dso_binary_type binary_type_symtab[] = { diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 857f707ac12b..ccd1caa40e11 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -109,7 +109,8 @@ struct symbol_conf { branch_callstack, has_filter, show_ref_callgraph, - hide_unresolved; + hide_unresolved, + raw_trace; const char *vmlinux_name, *kallsyms_name, *source_prefix, diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index b85ee55cca0c..bce5b1dac268 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -65,6 +65,7 @@ int tracing_data_put(struct tracing_data *tdata); struct addr_location; struct perf_session; +struct perf_stat_config; struct scripting_ops { const char *name; @@ -75,6 +76,9 @@ struct scripting_ops { struct perf_sample *sample, struct perf_evsel *evsel, struct addr_location *al); + void (*process_stat)(struct perf_stat_config *config, + struct perf_evsel *evsel, u64 tstamp); + void (*process_stat_interval)(u64 tstamp); int (*generate_script) (struct pevent *pevent, const char *outfile); }; diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index db8142ba7cb9..cf5e250bc78e 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -96,6 +96,16 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, MAP__FUNCTION, addr, &al); if (!al.map) { + /* + * We've seen cases (softice) where DWARF unwinder went + * through non executable mmaps, which we need to lookup + * in MAP__VARIABLE tree. + */ + thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, + MAP__VARIABLE, addr, &al); + } + + if (!al.map) { pr_debug("unwind: no map for %lx\n", (unsigned long)addr); return -1; } diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 3c258a0e4092..ee7e372297e5 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -319,6 +319,15 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui) thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, MAP__FUNCTION, ip, &al); + if (!al.map) { + /* + * We've seen cases (softice) where DWARF unwinder went + * through non executable mmaps, which we need to lookup + * in MAP__VARIABLE tree. + */ + thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, + MAP__VARIABLE, ip, &al); + } return al.map; } @@ -416,20 +425,19 @@ get_proc_name(unw_addr_space_t __maybe_unused as, static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, unw_word_t *data) { - struct addr_location al; + struct map *map; ssize_t size; - thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, - MAP__FUNCTION, addr, &al); - if (!al.map) { + map = find_map(addr, ui); + if (!map) { pr_debug("unwind: no map for %lx\n", (unsigned long)addr); return -1; } - if (!al.map->dso) + if (!map->dso) return -1; - size = dso__data_read_addr(al.map->dso, al.map, ui->machine, + size = dso__data_read_addr(map->dso, map, ui->machine, addr, (u8 *) data, sizeof(*data)); return !(size == sizeof(*data)); diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index aff0cfd83662..88b8f8d21f58 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -16,6 +16,8 @@ #include <linux/kernel.h> #include <unistd.h> #include "callchain.h" +#include "strlist.h" +#include <subcmd/exec-cmd.h> struct callchain_param callchain_param = { .mode = CHAIN_GRAPH_ABS, @@ -663,3 +665,28 @@ fetch_kernel_version(unsigned int *puint, char *str, *puint = (version << 16) + (patchlevel << 8) + sublevel; return 0; } + +const char *perf_tip(const char *dirpath) +{ + struct strlist *tips; + struct str_node *node; + char *tip = NULL; + struct strlist_config conf = { + .dirname = system_path(dirpath) , + }; + + tips = strlist__new("tips.txt", &conf); + if (tips == NULL || strlist__nr_entries(tips) == 1) { + tip = (char *)"Cannot find tips.txt file"; + goto out; + } + + node = strlist__entry(tips, random() % strlist__nr_entries(tips)); + if (asprintf(&tip, "Tip: %s", node->s) < 0) + tip = (char *)"Tip: get more memory! ;-)"; + +out: + strlist__delete(tips); + + return tip; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 4b519c59bdc3..fe915e616f9b 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -342,4 +342,6 @@ int fetch_kernel_version(unsigned int *puint, #define KVER_FMT "%d.%d.%d" #define KVER_PARAM(x) KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x) +const char *perf_tip(const char *dirpath); + #endif /* GIT_COMPAT_UTIL_H */ |