diff options
Diffstat (limited to 'tools/perf/util')
33 files changed, 580 insertions, 196 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 36437527dbb3..7dabde14ea54 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -478,7 +478,7 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); - if (addr < sym->start || addr > sym->end) + if (addr < sym->start || addr >= sym->end) return -ERANGE; offset = addr - sym->start; @@ -836,7 +836,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, end = map__rip_2objdump(map, sym->end); offset = line_ip - start; - if ((u64)line_ip < start || (u64)line_ip > end) + if ((u64)line_ip < start || (u64)line_ip >= end) offset = -1; else parsed_line = tmp2 + 1; @@ -966,7 +966,7 @@ fallback: kce.kcore_filename = symfs_filename; kce.addr = map__rip_2objdump(map, sym->start); kce.offs = sym->start; - kce.len = sym->end + 1 - sym->start; + kce.len = sym->end - sym->start; if (!kcore_extract__create(&kce)) { delete_extract = true; strlcpy(symfs_filename, kce.extract_filename, @@ -987,7 +987,7 @@ fallback: disassembler_style ? "-M " : "", disassembler_style ? disassembler_style : "", map__rip_2objdump(map, sym->start), - map__rip_2objdump(map, sym->end+1), + map__rip_2objdump(map, sym->end), symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", symbol_conf.annotate_src ? "-S" : "", symfs_filename, filename); diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 2a1f5a46543a..94cfefddf4db 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -65,6 +65,8 @@ struct callchain_param { enum chain_key key; }; +extern struct callchain_param callchain_param; + struct callchain_list { u64 ip; struct map_symbol ms; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 7eb7107731ec..5699e7e2a790 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -190,6 +190,32 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_HEADER_MAX }; +/* + * The kernel collects the number of events it couldn't send in a stretch and + * when possible sends this number in a PERF_RECORD_LOST event. The number of + * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while + * total_lost tells exactly how many events the kernel in fact lost, i.e. it is + * the sum of all struct lost_event.lost fields reported. + * + * The total_period is needed because by default auto-freq is used, so + * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get + * the total number of low level events, it is necessary to to sum all struct + * sample_event.period and stash the result in total_period. + */ +struct events_stats { + u64 total_period; + u64 total_non_filtered_period; + u64 total_lost; + u64 total_invalid_chains; + u32 nr_events[PERF_RECORD_HEADER_MAX]; + u32 nr_non_filtered_samples; + u32 nr_lost_warned; + u32 nr_unknown_events; + u32 nr_invalid_chains; + u32 nr_unknown_id; + u32 nr_unprocessable_samples; +}; + struct attr_event { struct perf_event_header header; struct perf_event_attr attr; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 3cebc9a8d52e..3c9e77d6b4c2 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1003,6 +1003,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) out_delete_threads: thread_map__delete(evlist->threads); + evlist->threads = NULL; return -1; } @@ -1175,11 +1176,51 @@ void perf_evlist__close(struct perf_evlist *evlist) } } +static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist) +{ + int err = -ENOMEM; + + /* + * Try reading /sys/devices/system/cpu/online to get + * an all cpus map. + * + * FIXME: -ENOMEM is the best we can do here, the cpu_map + * code needs an overhaul to properly forward the + * error, and we may not want to do that fallback to a + * default cpu identity map :-\ + */ + evlist->cpus = cpu_map__new(NULL); + if (evlist->cpus == NULL) + goto out; + + evlist->threads = thread_map__new_dummy(); + if (evlist->threads == NULL) + goto out_free_cpus; + + err = 0; +out: + return err; +out_free_cpus: + cpu_map__delete(evlist->cpus); + evlist->cpus = NULL; + goto out; +} + int perf_evlist__open(struct perf_evlist *evlist) { struct perf_evsel *evsel; int err; + /* + * Default: one fd per CPU, all threads, aka systemwide + * as sys_perf_event_open(cpu = -1, thread = -1) is EINVAL + */ + if (evlist->threads == NULL && evlist->cpus == NULL) { + err = perf_evlist__create_syswide_maps(evlist); + if (err < 0) + goto out_err; + } + perf_evlist__update_id_pos(evlist); evlist__for_each(evlist, evsel) { @@ -1276,8 +1317,14 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar sigaction(SIGUSR1, &act, NULL); } - if (target__none(target)) + if (target__none(target)) { + if (evlist->threads == NULL) { + fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n", + __func__, __LINE__); + goto out_close_pipes; + } evlist->threads->map[0] = evlist->workload.pid; + } close(child_ready_pipe[1]); close(go_pipe[0]); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index bd312b01e876..649b0c597283 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -117,6 +117,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, void *ucontext)); int perf_evlist__start_workload(struct perf_evlist *evlist); +struct option; + int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, int unset); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e0868a901c4a..2f9e68025ede 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -15,6 +15,8 @@ #include <linux/perf_event.h> #include <sys/resource.h> #include "asm/bug.h" +#include "callchain.h" +#include "cgroup.h" #include "evsel.h" #include "evlist.h" #include "util.h" @@ -32,6 +34,48 @@ static struct { bool cloexec; } perf_missing_features; +static int perf_evsel__no_extra_init(struct perf_evsel *evsel __maybe_unused) +{ + return 0; +} + +static void perf_evsel__no_extra_fini(struct perf_evsel *evsel __maybe_unused) +{ +} + +static struct { + size_t size; + int (*init)(struct perf_evsel *evsel); + void (*fini)(struct perf_evsel *evsel); +} perf_evsel__object = { + .size = sizeof(struct perf_evsel), + .init = perf_evsel__no_extra_init, + .fini = perf_evsel__no_extra_fini, +}; + +int perf_evsel__object_config(size_t object_size, + int (*init)(struct perf_evsel *evsel), + void (*fini)(struct perf_evsel *evsel)) +{ + + if (object_size == 0) + goto set_methods; + + if (perf_evsel__object.size > object_size) + return -EINVAL; + + perf_evsel__object.size = object_size; + +set_methods: + if (init != NULL) + perf_evsel__object.init = init; + + if (fini != NULL) + perf_evsel__object.fini = fini; + + return 0; +} + #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) int __perf_evsel__sample_size(u64 sample_type) @@ -116,16 +160,6 @@ void perf_evsel__calc_id_pos(struct perf_evsel *evsel) evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type); } -void hists__init(struct hists *hists) -{ - 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]; - hists->entries_collapsed = RB_ROOT; - hists->entries = RB_ROOT; - pthread_mutex_init(&hists->lock, NULL); -} - void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, enum perf_event_sample_format bit) { @@ -168,14 +202,14 @@ void perf_evsel__init(struct perf_evsel *evsel, evsel->unit = ""; evsel->scale = 1.0; INIT_LIST_HEAD(&evsel->node); - hists__init(&evsel->hists); + perf_evsel__object.init(evsel); evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); perf_evsel__calc_id_pos(evsel); } struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) { - struct perf_evsel *evsel = zalloc(sizeof(*evsel)); + struct perf_evsel *evsel = zalloc(perf_evsel__object.size); if (evsel != NULL) perf_evsel__init(evsel, attr, idx); @@ -185,7 +219,7 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) { - struct perf_evsel *evsel = zalloc(sizeof(*evsel)); + struct perf_evsel *evsel = zalloc(perf_evsel__object.size); if (evsel != NULL) { struct perf_event_attr attr = { @@ -692,7 +726,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) } } -int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) +static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) { int cpu, thread; @@ -780,13 +814,13 @@ int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) return evsel->counts != NULL ? 0 : -ENOMEM; } -void perf_evsel__free_fd(struct perf_evsel *evsel) +static void perf_evsel__free_fd(struct perf_evsel *evsel) { xyarray__delete(evsel->fd); evsel->fd = NULL; } -void perf_evsel__free_id(struct perf_evsel *evsel) +static void perf_evsel__free_id(struct perf_evsel *evsel) { xyarray__delete(evsel->sample_id); evsel->sample_id = NULL; @@ -817,16 +851,17 @@ void perf_evsel__exit(struct perf_evsel *evsel) assert(list_empty(&evsel->node)); perf_evsel__free_fd(evsel); perf_evsel__free_id(evsel); -} - -void perf_evsel__delete(struct perf_evsel *evsel) -{ - perf_evsel__exit(evsel); close_cgroup(evsel->cgrp); zfree(&evsel->group_name); if (evsel->tp_format) pevent_free_format(evsel->tp_format); zfree(&evsel->name); + perf_evsel__object.fini(evsel); +} + +void perf_evsel__delete(struct perf_evsel *evsel) +{ + perf_evsel__exit(evsel); free(evsel); } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 7bc314be6a7b..163c5604e5d1 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -7,8 +7,6 @@ #include <linux/perf_event.h> #include <linux/types.h> #include "xyarray.h" -#include "cgroup.h" -#include "hist.h" #include "symbol.h" struct perf_counts_values { @@ -43,6 +41,8 @@ struct perf_sample_id { u64 period; }; +struct cgroup_sel; + /** struct perf_evsel - event selector * * @name - Can be set to retain the original event name passed by the user, @@ -66,7 +66,6 @@ struct perf_evsel { struct perf_counts *prev_raw_counts; int idx; u32 ids; - struct hists hists; char *name; double scale; const char *unit; @@ -100,13 +99,16 @@ union u64_swap { u32 val32[2]; }; -#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) - struct cpu_map; +struct target; struct thread_map; struct perf_evlist; struct record_opts; +int perf_evsel__object_config(size_t object_size, + int (*init)(struct perf_evsel *evsel), + void (*fini)(struct perf_evsel *evsel)); + 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) @@ -153,12 +155,9 @@ const char *perf_evsel__name(struct perf_evsel *evsel); const char *perf_evsel__group_name(struct perf_evsel *evsel); int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); -int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); -void perf_evsel__free_fd(struct perf_evsel *evsel); -void perf_evsel__free_id(struct perf_evsel *evsel); void perf_evsel__free_counts(struct perf_evsel *evsel); void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); @@ -281,8 +280,6 @@ static inline int perf_evsel__read_scaled(struct perf_evsel *evsel, return __perf_evsel__read(evsel, ncpus, nthreads, true); } -void hists__init(struct hists *hists); - int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, struct perf_sample *sample); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 86569fa3651d..6e88b9e395df 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -3,6 +3,7 @@ #include "hist.h" #include "session.h" #include "sort.h" +#include "evlist.h" #include "evsel.h" #include "annotate.h" #include <math.h> @@ -14,13 +15,6 @@ static bool hists__filter_entry_by_thread(struct hists *hists, static bool hists__filter_entry_by_symbol(struct hists *hists, struct hist_entry *he); -struct callchain_param callchain_param = { - .mode = CHAIN_GRAPH_REL, - .min_percent = 0.5, - .order = ORDER_CALLEE, - .key = CCKEY_FUNCTION -}; - u16 hists__col_len(struct hists *hists, enum hist_column col) { return hists->col_len[col]; @@ -516,6 +510,7 @@ 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 hist_entry *he; if (mi == NULL) @@ -532,7 +527,7 @@ 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. */ - he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi, + he = __hists__add_entry(hists, al, iter->parent, NULL, mi, cost, cost, 0, true); if (!he) return -ENOMEM; @@ -546,13 +541,14 @@ iter_finish_mem_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) { struct perf_evsel *evsel = iter->evsel; + struct hists *hists = evsel__hists(evsel); struct hist_entry *he = iter->he; int err = -EINVAL; if (he == NULL) goto out; - hists__inc_nr_samples(&evsel->hists, he->filtered); + hists__inc_nr_samples(hists, he->filtered); err = hist_entry__append_callchain(he, iter->sample); @@ -618,6 +614,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 hist_entry *he = NULL; int i = iter->curr; int err = 0; @@ -631,12 +628,12 @@ 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. */ - he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL, + he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL, 1, 1, 0, true); if (he == NULL) return -ENOMEM; - hists__inc_nr_samples(&evsel->hists, he->filtered); + hists__inc_nr_samples(hists, he->filtered); out: iter->he = he; @@ -668,7 +665,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location struct perf_sample *sample = iter->sample; struct hist_entry *he; - he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, + he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, sample->period, sample->weight, sample->transaction, true); if (he == NULL) @@ -691,7 +688,7 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, iter->he = NULL; - hists__inc_nr_samples(&evsel->hists, he->filtered); + hists__inc_nr_samples(evsel__hists(evsel), he->filtered); return hist_entry__append_callchain(he, sample); } @@ -724,12 +721,13 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, struct addr_location *al) { struct perf_evsel *evsel = iter->evsel; + struct hists *hists = evsel__hists(evsel); struct perf_sample *sample = iter->sample; struct hist_entry **he_cache = iter->priv; struct hist_entry *he; int err = 0; - he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, + he = __hists__add_entry(hists, al, iter->parent, NULL, NULL, sample->period, sample->weight, sample->transaction, true); if (he == NULL) @@ -746,7 +744,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, */ callchain_cursor_commit(&callchain_cursor); - hists__inc_nr_samples(&evsel->hists, he->filtered); + hists__inc_nr_samples(hists, he->filtered); return err; } @@ -802,7 +800,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, } } - he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, + he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, sample->period, sample->weight, sample->transaction, false); if (he == NULL) @@ -1408,6 +1406,21 @@ int hists__link(struct hists *leader, struct hists *other) return 0; } + +size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp) +{ + struct perf_evsel *pos; + size_t ret = 0; + + evlist__for_each(evlist, pos) { + ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); + ret += events_stats__fprintf(&evsel__hists(pos)->stats, fp); + } + + return ret; +} + + u64 hists__total_period(struct hists *hists) { return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : @@ -1434,3 +1447,31 @@ int perf_hist_config(const char *var, const char *value) return 0; } + +static int hists_evsel__init(struct perf_evsel *evsel) +{ + 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]; + hists->entries_collapsed = RB_ROOT; + hists->entries = RB_ROOT; + pthread_mutex_init(&hists->lock, NULL); + return 0; +} + +/* + * XXX We probably need a hists_evsel__exit() to free the hist_entries + * stored in the rbtree... + */ + +int hists__init(void) +{ + int err = perf_evsel__object_config(sizeof(struct hists_evsel), + hists_evsel__init, NULL); + if (err) + fputs("FATAL ERROR: Couldn't setup hists class\n", stderr); + + return err; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 8c9c70e18cbb..d0ef9a19a744 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -4,12 +4,11 @@ #include <linux/types.h> #include <pthread.h> #include "callchain.h" +#include "evsel.h" #include "header.h" #include "color.h" #include "ui/progress.h" -extern struct callchain_param callchain_param; - struct hist_entry; struct addr_location; struct symbol; @@ -23,32 +22,6 @@ enum hist_filter { HIST_FILTER__HOST, }; -/* - * The kernel collects the number of events it couldn't send in a stretch and - * when possible sends this number in a PERF_RECORD_LOST event. The number of - * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while - * total_lost tells exactly how many events the kernel in fact lost, i.e. it is - * the sum of all struct lost_event.lost fields reported. - * - * The total_period is needed because by default auto-freq is used, so - * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get - * the total number of low level events, it is necessary to to sum all struct - * sample_event.period and stash the result in total_period. - */ -struct events_stats { - u64 total_period; - u64 total_non_filtered_period; - u64 total_lost; - u64 total_invalid_chains; - u32 nr_events[PERF_RECORD_HEADER_MAX]; - u32 nr_non_filtered_samples; - u32 nr_lost_warned; - u32 nr_unknown_events; - u32 nr_invalid_chains; - u32 nr_unknown_id; - u32 nr_unprocessable_samples; -}; - enum hist_column { HISTC_SYMBOL, HISTC_DSO, @@ -165,6 +138,7 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, float min_pcnt, FILE *fp); +size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp); void hists__filter_by_dso(struct hists *hists); void hists__filter_by_thread(struct hists *hists); @@ -185,6 +159,25 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *he); void hists__match(struct hists *leader, struct hists *other); int hists__link(struct hists *leader, struct hists *other); +struct hists_evsel { + struct perf_evsel evsel; + struct hists hists; +}; + +static inline struct perf_evsel *hists_to_evsel(struct hists *hists) +{ + struct hists_evsel *hevsel = container_of(hists, struct hists_evsel, hists); + return &hevsel->evsel; +} + +static inline struct hists *evsel__hists(struct perf_evsel *evsel) +{ + struct hists_evsel *hevsel = (struct hists_evsel *)evsel; + return &hevsel->hists; +} + +int hists__init(void); + struct perf_hpp { char *buf; size_t size; diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h index 97a800738226..6f19c548ecc0 100644 --- a/tools/perf/util/include/linux/string.h +++ b/tools/perf/util/include/linux/string.h @@ -1,4 +1,3 @@ #include <string.h> void *memdup(const void *src, size_t len); -int str_append(char **s, int *len, const char *a); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index b7d477fbda02..34fc7c8672e4 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -13,12 +13,18 @@ #include <symbol/kallsyms.h> #include "unwind.h" +static void dsos__init(struct dsos *dsos) +{ + INIT_LIST_HEAD(&dsos->head); + dsos->root = RB_ROOT; +} + int machine__init(struct machine *machine, const char *root_dir, pid_t pid) { map_groups__init(&machine->kmaps); RB_CLEAR_NODE(&machine->rb_node); - INIT_LIST_HEAD(&machine->user_dsos.head); - INIT_LIST_HEAD(&machine->kernel_dsos.head); + dsos__init(&machine->user_dsos); + dsos__init(&machine->kernel_dsos); machine->threads = RB_ROOT; INIT_LIST_HEAD(&machine->dead_threads); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index b7090596ac50..2137c4596ec7 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -556,7 +556,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, 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->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, @@ -664,7 +664,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, goto move_map; } - before->end = map->start - 1; + before->end = map->start; map_groups__insert(mg, before); if (verbose >= 2) map__fprintf(before, fp); @@ -678,7 +678,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, goto move_map; } - after->start = map->end + 1; + after->start = map->end; map_groups__insert(mg, after); if (verbose >= 2) map__fprintf(after, fp); @@ -752,7 +752,7 @@ struct map *maps__find(struct rb_root *maps, u64 ip) m = rb_entry(parent, struct map, rb_node); if (ip < m->start) p = &(*p)->rb_left; - else if (ip > m->end) + else if (ip >= m->end) p = &(*p)->rb_right; else return m; diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c index 706ce1a66169..fd4be94125fb 100644 --- a/tools/perf/util/ordered-events.c +++ b/tools/perf/util/ordered-events.c @@ -1,5 +1,6 @@ #include <linux/list.h> #include <linux/compiler.h> +#include <linux/string.h> #include "ordered-events.h" #include "evlist.h" #include "session.h" @@ -57,11 +58,45 @@ static void queue_event(struct ordered_events *oe, struct ordered_event *new) } } +static union perf_event *__dup_event(struct ordered_events *oe, + union perf_event *event) +{ + union perf_event *new_event = NULL; + + if (oe->cur_alloc_size < oe->max_alloc_size) { + new_event = memdup(event, event->header.size); + if (new_event) + oe->cur_alloc_size += event->header.size; + } + + return new_event; +} + +static union perf_event *dup_event(struct ordered_events *oe, + union perf_event *event) +{ + return oe->copy_on_queue ? __dup_event(oe, event) : event; +} + +static void free_dup_event(struct ordered_events *oe, union perf_event *event) +{ + if (oe->copy_on_queue) { + oe->cur_alloc_size -= event->header.size; + free(event); + } +} + #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event)) -static struct ordered_event *alloc_event(struct ordered_events *oe) +static struct ordered_event *alloc_event(struct ordered_events *oe, + union perf_event *event) { struct list_head *cache = &oe->cache; struct ordered_event *new = NULL; + union perf_event *new_event; + + new_event = dup_event(oe, event); + if (!new_event) + return NULL; if (!list_empty(cache)) { new = list_entry(cache->next, struct ordered_event, list); @@ -74,8 +109,10 @@ static struct ordered_event *alloc_event(struct ordered_events *oe) size_t size = MAX_SAMPLE_BUFFER * sizeof(*new); oe->buffer = malloc(size); - if (!oe->buffer) + if (!oe->buffer) { + free_dup_event(oe, new_event); return NULL; + } pr("alloc size %" PRIu64 "B (+%zu), max %" PRIu64 "B\n", oe->cur_alloc_size, size, oe->max_alloc_size); @@ -90,15 +127,17 @@ static struct ordered_event *alloc_event(struct ordered_events *oe) pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size); } + new->event = new_event; return new; } struct ordered_event * -ordered_events__new(struct ordered_events *oe, u64 timestamp) +ordered_events__new(struct ordered_events *oe, u64 timestamp, + union perf_event *event) { struct ordered_event *new; - new = alloc_event(oe); + new = alloc_event(oe, event); if (new) { new->timestamp = timestamp; queue_event(oe, new); @@ -111,6 +150,7 @@ void ordered_events__delete(struct ordered_events *oe, struct ordered_event *eve { list_move(&event->list, &oe->cache); oe->nr_events--; + free_dup_event(oe, event->event); } static int __ordered_events__flush(struct perf_session *s, @@ -240,6 +280,7 @@ void ordered_events__free(struct ordered_events *oe) event = list_entry(oe->to_free.next, struct ordered_event, list); list_del(&event->list); + free_dup_event(oe, event->event); free(event); } } diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h index 3b2f20542a01..7b8f9b011f38 100644 --- a/tools/perf/util/ordered-events.h +++ b/tools/perf/util/ordered-events.h @@ -34,9 +34,11 @@ struct ordered_events { int buffer_idx; unsigned int nr_events; enum oe_flush last_flush_type; + bool copy_on_queue; }; -struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp); +struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp, + union perf_event *event); void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event); int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, enum oe_flush how); @@ -48,4 +50,10 @@ void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size) { oe->max_alloc_size = size; } + +static inline +void ordered_events__set_copy_on_queue(struct ordered_events *oe, bool copy) +{ + oe->copy_on_queue = copy; +} #endif /* __ORDERED_EVENTS_H */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index d76aa30cb1fb..c659a3ca1283 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -6,7 +6,7 @@ #include "parse-options.h" #include "parse-events.h" #include "exec_cmd.h" -#include "linux/string.h" +#include "string.h" #include "symbol.h" #include "cache.h" #include "header.h" @@ -30,6 +30,15 @@ extern int parse_events_debug; #endif int parse_events_parse(void *data, void *scanner); +static struct perf_pmu_event_symbol *perf_pmu_events_list; +/* + * The variable indicates the number of supported pmu event symbols. + * 0 means not initialized and ready to init + * -1 means failed to init, don't try anymore + * >0 is the number of supported pmu event symbols + */ +static int perf_pmu_events_list_num; + static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { [PERF_COUNT_HW_CPU_CYCLES] = { .symbol = "cpu-cycles", @@ -863,30 +872,111 @@ int parse_events_name(struct list_head *list, char *name) return 0; } -static int parse_events__scanner(const char *str, void *data, int start_token); +static int +comp_pmu(const void *p1, const void *p2) +{ + struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; + struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; -static int parse_events_fixup(int ret, const char *str, void *data, - int start_token) + return strcmp(pmu1->symbol, pmu2->symbol); +} + +static void perf_pmu__parse_cleanup(void) { - char *o = strdup(str); - char *s = NULL; - char *t = o; - char *p; + if (perf_pmu_events_list_num > 0) { + struct perf_pmu_event_symbol *p; + int i; + + for (i = 0; i < perf_pmu_events_list_num; i++) { + p = perf_pmu_events_list + i; + free(p->symbol); + } + free(perf_pmu_events_list); + perf_pmu_events_list = NULL; + perf_pmu_events_list_num = 0; + } +} + +#define SET_SYMBOL(str, stype) \ +do { \ + p->symbol = str; \ + if (!p->symbol) \ + goto err; \ + p->type = stype; \ +} while (0) + +/* + * Read the pmu events list from sysfs + * Save it into perf_pmu_events_list + */ +static void perf_pmu__parse_init(void) +{ + + struct perf_pmu *pmu = NULL; + struct perf_pmu_alias *alias; int len = 0; - if (!o) - return ret; - while ((p = strsep(&t, ",")) != NULL) { - if (s) - str_append(&s, &len, ","); - str_append(&s, &len, "cpu/"); - str_append(&s, &len, p); - str_append(&s, &len, "/"); + pmu = perf_pmu__find("cpu"); + if ((pmu == NULL) || list_empty(&pmu->aliases)) { + perf_pmu_events_list_num = -1; + return; } - free(o); - if (!s) - return -ENOMEM; - return parse_events__scanner(s, data, start_token); + list_for_each_entry(alias, &pmu->aliases, list) { + if (strchr(alias->name, '-')) + len++; + len++; + } + perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len); + if (!perf_pmu_events_list) + return; + perf_pmu_events_list_num = len; + + len = 0; + list_for_each_entry(alias, &pmu->aliases, list) { + struct perf_pmu_event_symbol *p = perf_pmu_events_list + len; + char *tmp = strchr(alias->name, '-'); + + if (tmp != NULL) { + SET_SYMBOL(strndup(alias->name, tmp - alias->name), + PMU_EVENT_SYMBOL_PREFIX); + p++; + SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX); + len += 2; + } else { + SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL); + len++; + } + } + qsort(perf_pmu_events_list, len, + sizeof(struct perf_pmu_event_symbol), comp_pmu); + + return; +err: + perf_pmu__parse_cleanup(); +} + +enum perf_pmu_event_symbol_type +perf_pmu__parse_check(const char *name) +{ + struct perf_pmu_event_symbol p, *r; + + /* scan kernel pmu events from sysfs if needed */ + if (perf_pmu_events_list_num == 0) + perf_pmu__parse_init(); + /* + * name "cpu" could be prefix of cpu-cycles or cpu// events. + * cpu-cycles has been handled by hardcode. + * So it must be cpu// events, not kernel pmu event. + */ + if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu")) + return PMU_EVENT_SYMBOL_ERR; + + p.symbol = strdup(name); + r = bsearch(&p, perf_pmu_events_list, + (size_t) perf_pmu_events_list_num, + sizeof(struct perf_pmu_event_symbol), comp_pmu); + free(p.symbol); + return r ? r->type : PMU_EVENT_SYMBOL_ERR; } static int parse_events__scanner(const char *str, void *data, int start_token) @@ -909,8 +999,6 @@ static int parse_events__scanner(const char *str, void *data, int start_token) parse_events__flush_buffer(buffer, scanner); parse_events__delete_buffer(buffer, scanner); parse_events_lex_destroy(scanner); - if (ret && !strchr(str, '/')) - ret = parse_events_fixup(ret, str, data, start_token); return ret; } @@ -945,6 +1033,7 @@ int parse_events(struct perf_evlist *evlist, const char *str) int ret; ret = parse_events__scanner(str, &data, PE_START_EVENTS); + perf_pmu__parse_cleanup(); if (!ret) { int entries = data.idx - evlist->nr_entries; perf_evlist__splice_list_tail(evlist, &data.list, entries); diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index df094b4ed5ed..db2cf78ff0f3 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -35,6 +35,18 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); #define EVENTS_HELP_MAX (128*1024) +enum perf_pmu_event_symbol_type { + PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */ + PMU_EVENT_SYMBOL, /* normal style PMU event */ + PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */ + PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */ +}; + +struct perf_pmu_event_symbol { + char *symbol; + enum perf_pmu_event_symbol_type type; +}; + enum { PARSE_EVENTS__TERM_TYPE_NUM, PARSE_EVENTS__TERM_TYPE_STR, @@ -95,6 +107,8 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, void *ptr, char *type); int parse_events_add_pmu(struct list_head *list, int *idx, char *pmu , struct list_head *head_config); +enum perf_pmu_event_symbol_type +perf_pmu__parse_check(const char *name); void parse_events__set_leader(char *name, struct list_head *list); void parse_events_update_lists(struct list_head *list_event, struct list_head *list_all); diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 343299575b30..906630bbf8eb 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -51,6 +51,24 @@ static int str(yyscan_t scanner, int token) return token; } +static int pmu_str_check(yyscan_t scanner) +{ + YYSTYPE *yylval = parse_events_get_lval(scanner); + char *text = parse_events_get_text(scanner); + + yylval->str = strdup(text); + switch (perf_pmu__parse_check(text)) { + case PMU_EVENT_SYMBOL_PREFIX: + return PE_PMU_EVENT_PRE; + case PMU_EVENT_SYMBOL_SUFFIX: + return PE_PMU_EVENT_SUF; + case PMU_EVENT_SYMBOL: + return PE_KERNEL_PMU_EVENT; + default: + return PE_NAME; + } +} + static int sym(yyscan_t scanner, int type, int config) { YYSTYPE *yylval = parse_events_get_lval(scanner); @@ -178,6 +196,16 @@ alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_AL emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } + /* + * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately. + * Because the prefix cycles is mixed up with cpu-cycles. + * loads and stores are mixed up with cache event + */ +cycles-ct { return str(yyscanner, PE_KERNEL_PMU_EVENT); } +cycles-t { return str(yyscanner, PE_KERNEL_PMU_EVENT); } +mem-loads { return str(yyscanner, PE_KERNEL_PMU_EVENT); } +mem-stores { return str(yyscanner, PE_KERNEL_PMU_EVENT); } + L1-dcache|l1-d|l1d|L1-data | L1-icache|l1-i|l1i|L1-instruction | LLC|L2 | @@ -199,7 +227,7 @@ r{num_raw_hex} { return raw(yyscanner); } {num_hex} { return value(yyscanner, 16); } {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } -{name} { return str(yyscanner, PE_NAME); } +{name} { return pmu_str_check(yyscanner); } "/" { BEGIN(config); return '/'; } - { return '-'; } , { BEGIN(event); return ','; } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 55fab6ad609a..93c4c9fbc922 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -47,6 +47,7 @@ static inc_group_count(struct list_head *list, %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP %token PE_ERROR +%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT %type <num> PE_VALUE %type <num> PE_VALUE_SYM_HW %type <num> PE_VALUE_SYM_SW @@ -58,6 +59,7 @@ static inc_group_count(struct list_head *list, %type <str> PE_MODIFIER_EVENT %type <str> PE_MODIFIER_BP %type <str> PE_EVENT_NAME +%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT %type <num> value_sym %type <head> event_config %type <term> event_term @@ -220,6 +222,44 @@ PE_NAME '/' '/' ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL)); $$ = list; } +| +PE_KERNEL_PMU_EVENT sep_dc +{ + struct parse_events_evlist *data = _data; + struct list_head *head; + struct parse_events_term *term; + struct list_head *list; + + ALLOC_LIST(head); + ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, 1)); + list_add_tail(&term->list, head); + + ALLOC_LIST(list); + ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head)); + parse_events__free_terms(head); + $$ = list; +} +| +PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc +{ + struct parse_events_evlist *data = _data; + struct list_head *head; + struct parse_events_term *term; + struct list_head *list; + char pmu_name[128]; + snprintf(&pmu_name, 128, "%s-%s", $1, $3); + + ALLOC_LIST(head); + ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, + &pmu_name, 1)); + list_add_tail(&term->list, head); + + ALLOC_LIST(list); + ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head)); + parse_events__free_terms(head); + $$ = list; +} value_sym: PE_VALUE_SYM_HW diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 93a41ca96b8e..e243ad962a4d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -12,16 +12,6 @@ #include "parse-events.h" #include "cpumap.h" -#define UNIT_MAX_LEN 31 /* max length for event unit name */ - -struct perf_pmu_alias { - char *name; - struct list_head terms; /* HEAD struct parse_events_term -> list */ - struct list_head list; /* ELEM */ - char unit[UNIT_MAX_LEN+1]; - double scale; -}; - struct perf_pmu_format { char *name; int value; diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index fe90a012c003..fe9dfbee8eed 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -30,6 +30,16 @@ struct perf_pmu_info { double scale; }; +#define UNIT_MAX_LEN 31 /* max length for event unit name */ + +struct perf_pmu_alias { + char *name; + struct list_head terms; /* HEAD struct parse_events_term -> list */ + struct list_head list; /* ELEM */ + char unit[UNIT_MAX_LEN+1]; + double scale; +}; + struct perf_pmu *perf_pmu__find(const char *name); int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, struct list_head *head_terms); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 56ba07cce549..496f21cadd97 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -28,6 +28,7 @@ #include "../../perf.h" #include "../debug.h" +#include "../callchain.h" #include "../evsel.h" #include "../util.h" #include "../event.h" diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 883406f4b381..6702ac28754b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -532,17 +532,16 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event, return -EINVAL; } - new = ordered_events__new(oe, timestamp); + new = ordered_events__new(oe, timestamp, event); if (!new) { ordered_events__flush(s, tool, OE_FLUSH__HALF); - new = ordered_events__new(oe, timestamp); + new = ordered_events__new(oe, timestamp, event); } if (!new) return -ENOMEM; new->file_offset = file_offset; - new->event = event; return 0; } @@ -813,22 +812,6 @@ int perf_session__deliver_event(struct perf_session *session, dump_event(session, event, file_offset, sample); evsel = perf_evlist__id2evsel(session->evlist, sample->id); - if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) { - /* - * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here - * because the tools right now may apply filters, discarding - * some of the samples. For consistency, in the future we - * should have something like nr_filtered_samples and remove - * the sample->period from total_sample_period, etc, KISS for - * now tho. - * - * Also testing against NULL allows us to handle files without - * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the - * future probably it'll be a good idea to restrict event - * processing via perf_session to files with both set. - */ - hists__inc_nr_events(&evsel->hists, event->header.type); - } machine = perf_session__find_machine_for_cpumode(session, event, sample); @@ -1391,16 +1374,9 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) { - struct perf_evsel *pos; size_t ret = fprintf(fp, "Aggregated stats:\n"); ret += events_stats__fprintf(&session->stats, fp); - - evlist__for_each(session->evlist, pos) { - ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); - ret += events_stats__fprintf(&pos->hists.stats, fp); - } - return ret; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index ffb440462008..a4be851f1a90 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -2,7 +2,6 @@ #define __PERF_SESSION_H #include "trace-event.h" -#include "hist.h" #include "event.h" #include "header.h" #include "machine.h" diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 289df9d1e65a..4906cd81cb56 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1218,7 +1218,7 @@ static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, hse = container_of(fmt, struct hpp_sort_entry, hpp); if (!len) - len = hists__col_len(&evsel->hists, hse->se->se_width_idx); + len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name); } @@ -1233,7 +1233,7 @@ static int __sort__hpp_width(struct perf_hpp_fmt *fmt, hse = container_of(fmt, struct hpp_sort_entry, hpp); if (!len) - len = hists__col_len(&evsel->hists, hse->se->se_width_idx); + len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); return len; } diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index d87767f76903..6afd6106ceb5 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -357,27 +357,3 @@ void *memdup(const void *src, size_t len) return p; } - -/** - * str_append - reallocate string and append another - * @s: pointer to string pointer - * @len: pointer to len (initialized) - * @a: string to append. - */ -int str_append(char **s, int *len, const char *a) -{ - int olen = *s ? strlen(*s) : 0; - int nlen = olen + strlen(a) + 1; - if (*len < nlen) { - *len = *len * 2; - if (*len < nlen) - *len = nlen; - *s = realloc(*s, *len); - if (!*s) - return -ENOMEM; - if (olen == 0) - **s = 0; - } - strcat(*s, a); - return 0; -} diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index be84f7a9838b..078331140d8c 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -186,7 +186,7 @@ void symbols__fixup_end(struct rb_root *symbols) curr = rb_entry(nd, struct symbol, rb_node); if (prev->end == prev->start && prev->end != curr->start) - prev->end = curr->start - 1; + prev->end = curr->start; } /* Last entry */ @@ -207,7 +207,7 @@ void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { prev = curr; curr = rb_entry(nd, struct map, rb_node); - prev->end = curr->start - 1; + prev->end = curr->start; } /* @@ -229,7 +229,7 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) sym = ((void *)sym) + symbol_conf.priv_size; sym->start = start; - sym->end = len ? start + len - 1 : start; + sym->end = len ? start + len : start; sym->binding = binding; sym->namelen = namelen - 1; @@ -325,7 +325,7 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) if (ip < s->start) n = n->rb_left; - else if (ip > s->end) + else if (ip >= s->end) n = n->rb_right; else return s; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index bec4b7bd09de..eb2c19bf8d90 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -95,7 +95,7 @@ void symbols__delete(struct rb_root *symbols); static inline size_t symbol__size(const struct symbol *sym) { - return sym->end - sym->start + 1; + return sym->end - sym->start; } struct strlist; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index a9df7f2c6dc9..2b7b2d91c016 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -7,6 +7,7 @@ #include "util.h" #include "debug.h" #include "comm.h" +#include "unwind.h" int thread__init_map_groups(struct thread *thread, struct machine *machine) { @@ -37,6 +38,9 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->cpu = -1; INIT_LIST_HEAD(&thread->comm_list); + if (unwind__prepare_access(thread) < 0) + goto err_thread; + comm_str = malloc(32); if (!comm_str) goto err_thread; @@ -48,6 +52,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) goto err_thread; list_add(&comm->list, &thread->comm_list); + } return thread; @@ -69,6 +74,7 @@ void thread__delete(struct thread *thread) list_del(&comm->list); comm__free(comm); } + unwind__finish_access(thread); free(thread); } diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 5d3215912105..f93b9734735b 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -214,6 +214,17 @@ out_free_threads: goto out; } +struct thread_map *thread_map__new_dummy(void) +{ + struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); + + if (threads != NULL) { + threads->map[0] = -1; + threads->nr = 1; + } + return threads; +} + static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) { struct thread_map *threads = NULL, *nt; @@ -224,14 +235,8 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) struct strlist *slist; /* perf-stat expects threads to be generated even if tid not given */ - if (!tid_str) { - threads = malloc(sizeof(*threads) + sizeof(pid_t)); - if (threads != NULL) { - threads->map[0] = -1; - threads->nr = 1; - } - return threads; - } + if (!tid_str) + return thread_map__new_dummy(); slist = strlist__new(false, tid_str); if (!slist) diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 0cd8b3108084..95313f43cc0f 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -9,6 +9,7 @@ struct thread_map { pid_t map[]; }; +struct thread_map *thread_map__new_dummy(void); struct thread_map *thread_map__new_by_pid(pid_t pid); struct thread_map *thread_map__new_by_tid(pid_t tid); struct thread_map *thread_map__new_by_uid(uid_t uid); diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 92b56db52471..e060386165c5 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -24,6 +24,7 @@ #include <linux/list.h> #include <libunwind.h> #include <libunwind-ptrace.h> +#include "callchain.h" #include "thread.h" #include "session.h" #include "perf_regs.h" @@ -525,12 +526,12 @@ static unw_accessors_t accessors = { .get_proc_name = get_proc_name, }; -static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, - void *arg, int max_stack) +int unwind__prepare_access(struct thread *thread) { unw_addr_space_t addr_space; - unw_cursor_t c; - int ret; + + if (callchain_param.record_mode != CALLCHAIN_DWARF) + return 0; addr_space = unw_create_addr_space(&accessors, 0); if (!addr_space) { @@ -538,6 +539,33 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, return -ENOMEM; } + thread__set_priv(thread, addr_space); + + return 0; +} + +void unwind__finish_access(struct thread *thread) +{ + unw_addr_space_t addr_space; + + if (callchain_param.record_mode != CALLCHAIN_DWARF) + return; + + addr_space = thread__priv(thread); + unw_destroy_addr_space(addr_space); +} + +static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, + void *arg, int max_stack) +{ + unw_addr_space_t addr_space; + unw_cursor_t c; + int ret; + + addr_space = thread__priv(ui->thread); + if (addr_space == NULL) + return -1; + ret = unw_init_remote(&c, addr_space, ui); if (ret) display_error(ret); @@ -549,7 +577,6 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0; } - unw_destroy_addr_space(addr_space); return ret; } diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index f03061260b4e..c17c4855bdbc 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -4,6 +4,7 @@ #include <linux/types.h> #include "event.h" #include "symbol.h" +#include "thread.h" struct unwind_entry { struct map *map; @@ -21,6 +22,15 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, /* libunwind specific */ #ifdef HAVE_LIBUNWIND_SUPPORT int libunwind__arch_reg_id(int regnum); +int unwind__prepare_access(struct thread *thread); +void unwind__finish_access(struct thread *thread); +#else +static inline int unwind__prepare_access(struct thread *thread __maybe_unused) +{ + return 0; +} + +static inline void unwind__finish_access(struct thread *thread __maybe_unused) {} #endif #else static inline int @@ -33,5 +43,12 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, { return 0; } + +static inline int unwind__prepare_access(struct thread *thread __maybe_unused) +{ + return 0; +} + +static inline void unwind__finish_access(struct thread *thread __maybe_unused) {} #endif /* HAVE_DWARF_UNWIND_SUPPORT */ #endif /* __UNWIND_H */ diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 24e8d871b74e..d5eab3f3323f 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -14,6 +14,14 @@ #include <byteswap.h> #include <linux/kernel.h> #include <unistd.h> +#include "callchain.h" + +struct callchain_param callchain_param = { + .mode = CHAIN_GRAPH_REL, + .min_percent = 0.5, + .order = ORDER_CALLEE, + .key = CCKEY_FUNCTION +}; /* * XXX We need to find a better place for these things... |