diff options
Diffstat (limited to 'tools/perf/util')
55 files changed, 1417 insertions, 858 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 8052373bcd6a..5d4c45b76895 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -152,6 +152,8 @@ libperf-y += perf-hooks.o libperf-$(CONFIG_CXX) += c++/ CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" +CFLAGS_llvm-utils.o += -DPERF_INCLUDE_DIR="BUILD_STR($(perf_include_dir_SQ))" + # avoid compiler warnings in 32-bit mode CFLAGS_genelf_debug.o += -Wno-packed diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 536ee148bff8..71897689dacf 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -760,6 +760,15 @@ static int __symbol__account_cycles(struct annotation *notes, ch[offset].num_aggr++; ch[offset].cycles_aggr += cycles; + if (cycles > ch[offset].cycles_max) + ch[offset].cycles_max = cycles; + + if (ch[offset].cycles_min) { + if (cycles && cycles < ch[offset].cycles_min) + ch[offset].cycles_min = cycles; + } else + ch[offset].cycles_min = cycles; + if (!have_start && ch[offset].have_start) return 0; if (ch[offset].num) { @@ -953,8 +962,11 @@ void annotation__compute_ipc(struct annotation *notes, size_t size) if (ch->have_start) annotation__count_and_fill(notes, ch->start, offset, ch); al = notes->offsets[offset]; - if (al && ch->num_aggr) + if (al && ch->num_aggr) { al->cycles = ch->cycles_aggr / ch->num_aggr; + al->cycles_max = ch->cycles_max; + al->cycles_min = ch->cycles_min; + } notes->have_cycles = true; } } @@ -1263,6 +1275,9 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start max_percent = sample->percent; } + if (al->samples_nr > nr_percent) + nr_percent = al->samples_nr; + if (max_percent < min_pcnt) return -1; @@ -1950,6 +1965,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, u64 len; int width = symbol_conf.show_total_period ? 12 : 8; int graph_dotted_len; + char buf[512]; filename = strdup(dso->long_name); if (!filename) @@ -1962,8 +1978,11 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, len = symbol__size(sym); - if (perf_evsel__is_group_event(evsel)) + if (perf_evsel__is_group_event(evsel)) { width *= evsel->nr_members; + perf_evsel__group_desc(evsel, buf, sizeof(buf)); + evsel_name = buf; + } graph_dotted_len = printf(" %-*.*s| Source code & Disassembly of %s for %s (%" PRIu64 " samples)\n", width, width, symbol_conf.show_total_period ? "Period" : @@ -2483,13 +2502,38 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati else obj__printf(obj, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC"); - if (al->cycles) - obj__printf(obj, "%*" PRIu64 " ", + if (!notes->options->show_minmax_cycle) { + if (al->cycles) + obj__printf(obj, "%*" PRIu64 " ", ANNOTATION__CYCLES_WIDTH - 1, al->cycles); - else if (!show_title) - obj__printf(obj, "%*s", ANNOTATION__CYCLES_WIDTH, " "); - else - obj__printf(obj, "%*s ", ANNOTATION__CYCLES_WIDTH - 1, "Cycle"); + else if (!show_title) + obj__printf(obj, "%*s", + ANNOTATION__CYCLES_WIDTH, " "); + else + obj__printf(obj, "%*s ", + ANNOTATION__CYCLES_WIDTH - 1, + "Cycle"); + } else { + if (al->cycles) { + char str[32]; + + scnprintf(str, sizeof(str), + "%" PRIu64 "(%" PRIu64 "/%" PRIu64 ")", + al->cycles, al->cycles_min, + al->cycles_max); + + obj__printf(obj, "%*s ", + ANNOTATION__MINMAX_CYCLES_WIDTH - 1, + str); + } else if (!show_title) + obj__printf(obj, "%*s", + ANNOTATION__MINMAX_CYCLES_WIDTH, + " "); + else + obj__printf(obj, "%*s ", + ANNOTATION__MINMAX_CYCLES_WIDTH - 1, + "Cycle(min/max)"); + } } obj__printf(obj, " "); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index f28a9e43421d..5080b6dd98b8 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -61,6 +61,7 @@ bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2); #define ANNOTATION__IPC_WIDTH 6 #define ANNOTATION__CYCLES_WIDTH 6 +#define ANNOTATION__MINMAX_CYCLES_WIDTH 19 struct annotation_options { bool hide_src_code, @@ -69,7 +70,8 @@ struct annotation_options { show_linenr, show_nr_jumps, show_nr_samples, - show_total_period; + show_total_period, + show_minmax_cycle; u8 offset_level; }; @@ -105,6 +107,8 @@ struct annotation_line { int jump_sources; float ipc; u64 cycles; + u64 cycles_max; + u64 cycles_min; size_t privsize; char *path; u32 idx; @@ -186,6 +190,8 @@ struct cyc_hist { u64 start; u64 cycles; u64 cycles_aggr; + u64 cycles_max; + u64 cycles_min; u32 num; u32 num_aggr; u8 have_start; @@ -239,6 +245,9 @@ struct annotation { static inline int annotation__cycles_width(struct annotation *notes) { + if (notes->have_cycles && notes->options->show_minmax_cycle) + return ANNOTATION__IPC_WIDTH + ANNOTATION__MINMAX_CYCLES_WIDTH; + return notes->have_cycles ? ANNOTATION__IPC_WIDTH + ANNOTATION__CYCLES_WIDTH : 0; } diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 857de69a5361..d056447520a2 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -1679,7 +1679,7 @@ struct sym_args { static bool kern_sym_match(struct sym_args *args, const char *name, char type) { /* A function with the same name, and global or the n'th found or any */ - return symbol_type__is_a(type, MAP__FUNCTION) && + return kallsyms__is_function(type) && !strcmp(name, args->name) && ((args->global && isupper(type)) || (args->selected && ++(args->cnt) == args->idx) || @@ -1784,7 +1784,7 @@ static int find_entire_kern_cb(void *arg, const char *name __maybe_unused, { struct sym_args *args = arg; - if (!symbol_type__is_a(type, MAP__FUNCTION)) + if (!kallsyms__is_function(type)) return 0; if (!args->started) { @@ -1915,7 +1915,7 @@ static void print_duplicate_syms(struct dso *dso, const char *sym_name) pr_err("Multiple symbols with name '%s'\n", sym_name); - sym = dso__first_symbol(dso, MAP__FUNCTION); + sym = dso__first_symbol(dso); while (sym) { if (dso_sym_match(sym, sym_name, &cnt, -1)) { pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n", @@ -1945,7 +1945,7 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start, *start = 0; *size = 0; - sym = dso__first_symbol(dso, MAP__FUNCTION); + sym = dso__first_symbol(dso); while (sym) { if (*start) { if (!*size) @@ -1972,8 +1972,8 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start, static int addr_filter__entire_dso(struct addr_filter *filt, struct dso *dso) { - struct symbol *first_sym = dso__first_symbol(dso, MAP__FUNCTION); - struct symbol *last_sym = dso__last_symbol(dso, MAP__FUNCTION); + struct symbol *first_sym = dso__first_symbol(dso); + struct symbol *last_sym = dso__last_symbol(dso); if (!first_sym || !last_sym) { pr_err("Failed to determine filter for %s\nNo symbols found.\n", diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index af7ad814b2c3..cee658733e2c 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -66,7 +66,7 @@ bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name) } obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name); - if (IS_ERR(obj)) { + if (IS_ERR_OR_NULL(obj)) { pr_debug("bpf: failed to load buffer\n"); return ERR_PTR(-EINVAL); } @@ -102,14 +102,14 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source) pr_debug("bpf: successfull builtin compilation\n"); obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); - if (!IS_ERR(obj) && llvm_param.dump_obj) + if (!IS_ERR_OR_NULL(obj) && llvm_param.dump_obj) llvm__dump_obj(filename, obj_buf, obj_buf_sz); free(obj_buf); } else obj = bpf_object__open(filename); - if (IS_ERR(obj)) { + if (IS_ERR_OR_NULL(obj)) { pr_debug("bpf: failed to load %s\n", filename); return obj; } diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 537eadd81914..04b1d53e4bf9 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -47,9 +47,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, return -1; } - thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, sample->ip, &al); - - if (al.map != NULL) + if (thread__find_map(thread, sample->cpumode, sample->ip, &al)) al.map->dso->hit = 1; thread__put(thread); diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 84eb9393c7db..5ac157056cdf 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -707,6 +707,14 @@ struct perf_config_set *perf_config_set__new(void) return set; } +static int perf_config__init(void) +{ + if (config_set == NULL) + config_set = perf_config_set__new(); + + return config_set == NULL; +} + int perf_config(config_fn_t fn, void *data) { int ret = 0; @@ -714,7 +722,7 @@ int perf_config(config_fn_t fn, void *data) struct perf_config_section *section; struct perf_config_item *item; - if (config_set == NULL) + if (config_set == NULL && perf_config__init()) return -1; perf_config_set__for_each_entry(config_set, section, item) { @@ -735,12 +743,6 @@ int perf_config(config_fn_t fn, void *data) return ret; } -void perf_config__init(void) -{ - if (config_set == NULL) - config_set = perf_config_set__new(); -} - void perf_config__exit(void) { perf_config_set__delete(config_set); diff --git a/tools/perf/util/config.h b/tools/perf/util/config.h index baf82bf227ac..bd0a5897c76a 100644 --- a/tools/perf/util/config.h +++ b/tools/perf/util/config.h @@ -38,7 +38,6 @@ struct perf_config_set *perf_config_set__new(void); void perf_config_set__delete(struct perf_config_set *set); int perf_config_set__collect(struct perf_config_set *set, const char *file_name, const char *var, const char *value); -void perf_config__init(void); void perf_config__exit(void); void perf_config__refresh(void); diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index c8b98fa22997..4d5fc374e730 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -96,11 +96,19 @@ int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder, /* Nothing to do, might as well just return */ if (decoder->packet_count == 0) return 0; + /* + * The queueing process in function cs_etm_decoder__buffer_packet() + * increments the tail *before* using it. This is somewhat counter + * intuitive but it has the advantage of centralizing tail management + * at a single location. Because of that we need to follow the same + * heuristic with the head, i.e we increment it before using its + * value. Otherwise the first element of the packet queue is not + * used. + */ + decoder->head = (decoder->head + 1) & (MAX_BUFFER - 1); *packet = decoder->packet_buffer[decoder->head]; - decoder->head = (decoder->head + 1) & (MAX_BUFFER - 1); - decoder->packet_count--; return 1; diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 40020b1ca54f..822ba915d144 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -239,6 +239,7 @@ static void cs_etm__free(struct perf_session *session) for (i = 0; i < aux->num_cpu; i++) zfree(&aux->metadata[i]); + thread__zput(aux->unknown_thread); zfree(&aux->metadata); zfree(&aux); } @@ -269,9 +270,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u64 address, thread = etmq->etm->unknown_thread; } - thread__find_addr_map(thread, cpumode, MAP__FUNCTION, address, &al); - - if (!al.map || !al.map->dso) + if (!thread__find_map(thread, cpumode, address, &al) || !al.map->dso) return 0; if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR && @@ -612,8 +611,8 @@ cs_etm__get_trace(struct cs_etm_buffer *buff, struct cs_etm_queue *etmq) return buff->len; } -static void cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm, - struct auxtrace_queue *queue) +static void cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm, + struct auxtrace_queue *queue) { struct cs_etm_queue *etmq = queue->priv; @@ -1357,6 +1356,23 @@ int cs_etm__process_auxtrace_info(union perf_event *event, etm->auxtrace.free = cs_etm__free; session->auxtrace = &etm->auxtrace; + etm->unknown_thread = thread__new(999999999, 999999999); + if (!etm->unknown_thread) + goto err_free_queues; + + /* + * Initialize list node so that at thread__zput() we can avoid + * segmentation fault at list_del_init(). + */ + INIT_LIST_HEAD(&etm->unknown_thread->node); + + err = thread__set_comm(etm->unknown_thread, "unknown", 0); + if (err) + goto err_delete_thread; + + if (thread__init_map_groups(etm->unknown_thread, etm->machine)) + goto err_delete_thread; + if (dump_trace) { cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu); return 0; @@ -1371,16 +1387,18 @@ int cs_etm__process_auxtrace_info(union perf_event *event, err = cs_etm__synth_events(etm, session); if (err) - goto err_free_queues; + goto err_delete_thread; err = auxtrace_queues__process_index(&etm->queues, session); if (err) - goto err_free_queues; + goto err_delete_thread; etm->data_queued = etm->queues.populated; return 0; +err_delete_thread: + thread__zput(etm->unknown_thread); err_free_queues: auxtrace_queues__free(&etm->queues); session->auxtrace = NULL; diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index b0c2b5c5d337..7123746edcf4 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -247,9 +247,9 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al, *dso_db_id = dso->db_id; if (!al->sym) { - al->sym = symbol__new(al->addr, 0, 0, "unknown"); + al->sym = symbol__new(al->addr, 0, 0, 0, "unknown"); if (al->sym) - dso__insert_symbol(dso, al->map->type, al->sym); + dso__insert_symbol(dso, al->sym); } if (al->sym) { @@ -315,8 +315,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe, al.addr = node->ip; if (al.map && !al.sym) - al.sym = dso__find_symbol(al.map->dso, MAP__FUNCTION, - al.addr); + al.sym = dso__find_symbol(al.map->dso, al.addr); db_ids_from_al(dbe, &al, &dso_db_id, &sym_db_id, &offset); diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 36ef45b2e89d..cdfc2e5f55f5 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1014,7 +1014,7 @@ struct map *dso__new_map(const char *name) struct dso *dso = dso__new(name); if (dso) - map = map__new2(0, dso, MAP__FUNCTION); + map = map__new2(0, dso); return map; } @@ -1176,19 +1176,19 @@ int dso__name_len(const struct dso *dso) return dso->short_name_len; } -bool dso__loaded(const struct dso *dso, enum map_type type) +bool dso__loaded(const struct dso *dso) { - return dso->loaded & (1 << type); + return dso->loaded; } -bool dso__sorted_by_name(const struct dso *dso, enum map_type type) +bool dso__sorted_by_name(const struct dso *dso) { - return dso->sorted_by_name & (1 << type); + return dso->sorted_by_name; } -void dso__set_sorted_by_name(struct dso *dso, enum map_type type) +void dso__set_sorted_by_name(struct dso *dso) { - dso->sorted_by_name |= (1 << type); + dso->sorted_by_name = true; } struct dso *dso__new(const char *name) @@ -1196,12 +1196,10 @@ struct dso *dso__new(const char *name) struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); if (dso != NULL) { - int i; strcpy(dso->name, name); dso__set_long_name(dso, dso->name, false); dso__set_short_name(dso, dso->name, false); - for (i = 0; i < MAP__NR_TYPES; ++i) - dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; + dso->symbols = dso->symbol_names = RB_ROOT; dso->data.cache = RB_ROOT; dso->inlined_nodes = RB_ROOT; dso->srclines = RB_ROOT; @@ -1231,8 +1229,6 @@ struct dso *dso__new(const char *name) void dso__delete(struct dso *dso) { - int i; - if (!RB_EMPTY_NODE(&dso->rb_node)) pr_err("DSO %s is still in rbtree when being deleted!\n", dso->long_name); @@ -1240,8 +1236,7 @@ void dso__delete(struct dso *dso) /* free inlines first, as they reference symbols */ inlines__tree_delete(&dso->inlined_nodes); srcline__tree_delete(&dso->srclines); - for (i = 0; i < MAP__NR_TYPES; ++i) - symbols__delete(&dso->symbols[i]); + symbols__delete(&dso->symbols); if (dso->short_name_allocated) { zfree((char **)&dso->short_name); @@ -1451,9 +1446,7 @@ size_t __dsos__fprintf(struct list_head *head, FILE *fp) size_t ret = 0; list_for_each_entry(pos, head, node) { - int i; - for (i = 0; i < MAP__NR_TYPES; ++i) - ret += dso__fprintf(pos, i, fp); + ret += dso__fprintf(pos, fp); } return ret; @@ -1467,18 +1460,17 @@ size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) return fprintf(fp, "%s", sbuild_id); } -size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) +size_t dso__fprintf(struct dso *dso, FILE *fp) { struct rb_node *nd; size_t ret = fprintf(fp, "dso: %s (", dso->short_name); if (dso->short_name != dso->long_name) ret += fprintf(fp, "%s, ", dso->long_name); - ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], - dso__loaded(dso, type) ? "" : "NOT "); + ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT "); ret += dso__fprintf_buildid(dso, fp); ret += fprintf(fp, ")\n"); - for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&dso->symbols); nd; nd = rb_next(nd)) { struct symbol *pos = rb_entry(nd, struct symbol, rb_node); ret += symbol__fprintf(pos, fp); } diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index c229dbe0277a..ef69de2e69ea 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -140,14 +140,14 @@ struct dso { struct list_head node; struct rb_node rb_node; /* rbtree node sorted by long name */ struct rb_root *root; /* root of rbtree that rb_node is in */ - struct rb_root symbols[MAP__NR_TYPES]; - struct rb_root symbol_names[MAP__NR_TYPES]; + struct rb_root symbols; + struct rb_root symbol_names; struct rb_root inlined_nodes; struct rb_root srclines; struct { u64 addr; struct symbol *symbol; - } last_find_result[MAP__NR_TYPES]; + } last_find_result; void *a2l; char *symsrc_filename; unsigned int a2l_fails; @@ -164,8 +164,8 @@ struct dso { u8 short_name_allocated:1; u8 long_name_allocated:1; u8 is_64_bit:1; - u8 sorted_by_name; - u8 loaded; + bool sorted_by_name; + bool loaded; u8 rel; u8 build_id[BUILD_ID_SIZE]; u64 text_offset; @@ -202,14 +202,13 @@ struct dso { * @dso: the 'struct dso *' in which symbols itereated * @pos: the 'struct symbol *' to use as a loop cursor * @n: the 'struct rb_node *' to use as a temporary storage - * @type: the 'enum map_type' type of symbols */ -#define dso__for_each_symbol(dso, pos, n, type) \ - symbols__for_each_entry(&(dso)->symbols[(type)], pos, n) +#define dso__for_each_symbol(dso, pos, n) \ + symbols__for_each_entry(&(dso)->symbols, pos, n) -static inline void dso__set_loaded(struct dso *dso, enum map_type type) +static inline void dso__set_loaded(struct dso *dso) { - dso->loaded |= (1 << type); + dso->loaded = true; } struct dso *dso__new(const char *name); @@ -231,11 +230,16 @@ static inline void __dso__zput(struct dso **dso) #define dso__zput(dso) __dso__zput(&dso) -bool dso__loaded(const struct dso *dso, enum map_type type); +bool dso__loaded(const struct dso *dso); -bool dso__sorted_by_name(const struct dso *dso, enum map_type type); -void dso__set_sorted_by_name(struct dso *dso, enum map_type type); -void dso__sort_by_name(struct dso *dso, enum map_type type); +static inline bool dso__has_symbols(const struct dso *dso) +{ + return !RB_EMPTY_ROOT(&dso->symbols); +} + +bool dso__sorted_by_name(const struct dso *dso); +void dso__set_sorted_by_name(struct dso *dso); +void dso__sort_by_name(struct dso *dso); void dso__set_build_id(struct dso *dso, void *build_id); bool dso__build_id_equal(const struct dso *dso, u8 *build_id); @@ -349,9 +353,8 @@ size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, size_t __dsos__fprintf(struct list_head *head, FILE *fp); size_t dso__fprintf_buildid(struct dso *dso, FILE *fp); -size_t dso__fprintf_symbols_by_name(struct dso *dso, - enum map_type type, FILE *fp); -size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); +size_t dso__fprintf_symbols_by_name(struct dso *dso, FILE *fp); +size_t dso__fprintf(struct dso *dso, FILE *fp); static inline bool dso__is_vmlinux(struct dso *dso) { diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 4c842762e3f2..59f38c7693f8 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -93,6 +93,37 @@ int perf_env__read_cpu_topology_map(struct perf_env *env) return 0; } +static int perf_env__read_arch(struct perf_env *env) +{ + struct utsname uts; + + if (env->arch) + return 0; + + if (!uname(&uts)) + env->arch = strdup(uts.machine); + + return env->arch ? 0 : -ENOMEM; +} + +static int perf_env__read_nr_cpus_avail(struct perf_env *env) +{ + if (env->nr_cpus_avail == 0) + env->nr_cpus_avail = cpu__max_present_cpu(); + + return env->nr_cpus_avail ? 0 : -ENOENT; +} + +const char *perf_env__raw_arch(struct perf_env *env) +{ + return env && !perf_env__read_arch(env) ? env->arch : "unknown"; +} + +int perf_env__nr_cpus_avail(struct perf_env *env) +{ + return env && !perf_env__read_nr_cpus_avail(env) ? env->nr_cpus_avail : 0; +} + void cpu_cache_level__free(struct cpu_cache_level *cache) { free(cache->type); diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index c4ef2e523367..1f3ccc368530 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -76,4 +76,7 @@ int perf_env__read_cpu_topology_map(struct perf_env *env); void cpu_cache_level__free(struct cpu_cache_level *cache); const char *perf_env__arch(struct perf_env *env); +const char *perf_env__raw_arch(struct perf_env *env); +int perf_env__nr_cpus_avail(struct perf_env *env); + #endif /* __PERF_ENV_H */ diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 98ff3a6a3d50..0c8ecf0c78a4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -88,10 +88,10 @@ static const char *perf_ns__name(unsigned int id) return perf_ns__names[id]; } -static int perf_tool__process_synth_event(struct perf_tool *tool, - union perf_event *event, - struct machine *machine, - perf_event__handler_t process) +int perf_tool__process_synth_event(struct perf_tool *tool, + union perf_event *event, + struct machine *machine, + perf_event__handler_t process) { struct perf_sample synth_sample = { .pid = -1, @@ -464,8 +464,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool, { int rc = 0; struct map *pos; - struct map_groups *kmaps = &machine->kmaps; - struct maps *maps = &kmaps->maps[MAP__FUNCTION]; + struct maps *maps = machine__kernel_maps(machine); union perf_event *event = zalloc((sizeof(event->mmap) + machine->id_hdr_size)); if (event == NULL) { @@ -488,7 +487,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool, for (pos = maps__first(maps); pos; pos = map__next(pos)) { size_t size; - if (__map__is_kernel(pos)) + if (!__map__is_kmodule(pos)) continue; size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); @@ -869,7 +868,7 @@ static int find_symbol_cb(void *arg, const char *name, char type, * Must be a function or at least an alias, as in PARISC64, where "_text" is * an 'A' to the same address as "_stext". */ - if (!(symbol_type__is_a(type, MAP__FUNCTION) || + if (!(kallsyms__is_function(type) || type == 'A') || strcmp(name, args->name)) return 0; @@ -889,9 +888,16 @@ int kallsyms__get_function_start(const char *kallsyms_filename, return 0; } -int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, - perf_event__handler_t process, - struct machine *machine) +int __weak perf_event__synthesize_extra_kmaps(struct perf_tool *tool __maybe_unused, + perf_event__handler_t process __maybe_unused, + struct machine *machine __maybe_unused) +{ + return 0; +} + +static int __perf_event__synthesize_kernel_mmap(struct perf_tool *tool, + perf_event__handler_t process, + struct machine *machine) { size_t size; struct map *map = machine__kernel_map(machine); @@ -944,6 +950,19 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, return err; } +int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, + perf_event__handler_t process, + struct machine *machine) +{ + int err; + + err = __perf_event__synthesize_kernel_mmap(tool, process, machine); + if (err < 0) + return err; + + return perf_event__synthesize_extra_kmaps(tool, process, machine); +} + int perf_event__synthesize_thread_map2(struct perf_tool *tool, struct thread_map *threads, perf_event__handler_t process, @@ -1489,9 +1508,8 @@ int perf_event__process(struct perf_tool *tool __maybe_unused, return machine__process_event(machine, event, sample); } -void thread__find_addr_map(struct thread *thread, u8 cpumode, - enum map_type type, u64 addr, - struct addr_location *al) +struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, + struct addr_location *al) { struct map_groups *mg = thread->mg; struct machine *machine = mg->machine; @@ -1505,7 +1523,7 @@ void thread__find_addr_map(struct thread *thread, u8 cpumode, if (machine == NULL) { al->map = NULL; - return; + return NULL; } if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { @@ -1533,10 +1551,10 @@ void thread__find_addr_map(struct thread *thread, u8 cpumode, !perf_host) al->filtered |= (1 << HIST_FILTER__HOST); - return; + return NULL; } try_again: - al->map = map_groups__find(mg, type, al->addr); + al->map = map_groups__find(mg, al->addr); if (al->map == NULL) { /* * If this is outside of all known maps, and is a negative @@ -1563,17 +1581,17 @@ try_again: map__load(al->map); al->addr = al->map->map_ip(al->map, al->addr); } + + return al->map; } -void thread__find_addr_location(struct thread *thread, - u8 cpumode, enum map_type type, u64 addr, - struct addr_location *al) +struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode, + u64 addr, struct addr_location *al) { - thread__find_addr_map(thread, cpumode, type, addr, al); - if (al->map != NULL) + al->sym = NULL; + if (thread__find_map(thread, cpumode, addr, al)) al->sym = map__find_symbol(al->map, al->addr); - else - al->sym = NULL; + return al->sym; } /* @@ -1590,7 +1608,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al, return -1; dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); - thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, sample->ip, al); + thread__find_map(thread, sample->cpumode, sample->ip, al); dump_printf(" ...... dso: %s\n", al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : "<not found>"); @@ -1669,10 +1687,7 @@ bool sample_addr_correlates_sym(struct perf_event_attr *attr) void thread__resolve(struct thread *thread, struct addr_location *al, struct perf_sample *sample) { - thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, sample->addr, al); - if (!al->map) - thread__find_addr_map(thread, sample->cpumode, MAP__VARIABLE, - sample->addr, al); + thread__find_map(thread, sample->cpumode, sample->addr, al); al->cpu = sample->cpu; al->sym = NULL; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 0f794744919c..bfa60bcafbde 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -750,6 +750,10 @@ int perf_event__process_exit(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); +int perf_tool__process_synth_event(struct perf_tool *tool, + union perf_event *event, + struct machine *machine, + perf_event__handler_t process); int perf_event__process(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, @@ -796,6 +800,10 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, bool mmap_data, unsigned int proc_map_timeout); +int perf_event__synthesize_extra_kmaps(struct perf_tool *tool, + perf_event__handler_t process, + struct machine *machine); + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index a59281d64368..e7a4b31a84fb 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1795,3 +1795,18 @@ bool perf_evlist__exclude_kernel(struct perf_evlist *evlist) return true; } + +/* + * Events in data file are not collect in groups, but we still want + * the group display. Set the artificial group and set the leader's + * forced_leader flag to notify the display code. + */ +void perf_evlist__force_leader(struct perf_evlist *evlist) +{ + if (!evlist->nr_groups) { + struct perf_evsel *leader = perf_evlist__first(evlist); + + perf_evlist__set_leader(evlist); + leader->forced_leader = true; + } +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 6c41b2f78713..dc66436add98 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -309,4 +309,7 @@ struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist, union perf_event *event); bool perf_evlist__exclude_kernel(struct perf_evlist *evlist); + +void perf_evlist__force_leader(struct perf_evlist *evlist); + #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 4cd2cf93f726..150db5ed7400 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2862,7 +2862,7 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, return scnprintf(msg, size, "Not enough memory to setup event with callchain.\n" "Hint: Try tweaking /proc/sys/kernel/perf_event_max_stack\n" - "Hint: Current value: %d", sysctl_perf_event_max_stack); + "Hint: Current value: %d", sysctl__max_stack()); break; case ENODEV: if (target->cpu_list) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 92ec009a292d..b13f5f234c8f 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -127,6 +127,7 @@ struct perf_evsel { bool precise_max; bool ignore_missing_thread; bool forced_leader; + bool use_uncore_alias; /* parse modifier helper */ int exclude_GH; int nr_members; diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c index c540d47583e7..aafbe54fd3fa 100644 --- a/tools/perf/util/genelf.c +++ b/tools/perf/util/genelf.c @@ -114,7 +114,7 @@ gen_build_id(struct buildid_note *note, fd = open("/dev/urandom", O_RDONLY); if (fd == -1) - err(1, "cannot access /dev/urandom for builid"); + err(1, "cannot access /dev/urandom for buildid"); sret = read(fd, note->build_id, sz); diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index 72db2744876d..7f0c83b6332b 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -335,8 +335,7 @@ static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip) if (!thread) return -1; - thread__find_addr_map(thread, cpumode, MAP__FUNCTION, ip, &al); - if (!al.map || !al.map->dso) + if (!thread__find_map(thread, cpumode, ip, &al) || !al.map->dso) goto out_put; len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf, diff --git a/tools/perf/util/intel-pt-decoder/insn.h b/tools/perf/util/intel-pt-decoder/insn.h index e23578c7b1be..2669c9f748e4 100644 --- a/tools/perf/util/intel-pt-decoder/insn.h +++ b/tools/perf/util/intel-pt-decoder/insn.h @@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn) return insn_offset_displacement(insn) + insn->displacement.nbytes; } +#define POP_SS_OPCODE 0x1f +#define MOV_SREG_OPCODE 0x8e + +/* + * Intel SDM Vol.3A 6.8.3 states; + * "Any single-step trap that would be delivered following the MOV to SS + * instruction or POP to SS instruction (because EFLAGS.TF is 1) is + * suppressed." + * This function returns true if @insn is MOV SS or POP SS. On these + * instructions, single stepping is suppressed. + */ +static inline int insn_masking_exception(struct insn *insn) +{ + return insn->opcode.bytes[0] == POP_SS_OPCODE || + (insn->opcode.bytes[0] == MOV_SREG_OPCODE && + X86_MODRM_REG(insn->modrm.bytes[0]) == 2); +} + #endif /* _ASM_X86_INSN_H */ diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 0effaff57020..492986a25ef6 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -442,8 +442,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, } while (1) { - thread__find_addr_map(thread, cpumode, MAP__FUNCTION, *ip, &al); - if (!al.map || !al.map->dso) + if (!thread__find_map(thread, cpumode, *ip, &al) || !al.map->dso) return -EINVAL; if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR && @@ -596,8 +595,7 @@ static int __intel_pt_pgd_ip(uint64_t ip, void *data) if (!thread) return -EINVAL; - thread__find_addr_map(thread, cpumode, MAP__FUNCTION, ip, &al); - if (!al.map || !al.map->dso) + if (!thread__find_map(thread, cpumode, ip, &al) || !al.map->dso) return -EINVAL; offset = al.map->map_ip(al.map, ip); @@ -1565,7 +1563,7 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip) if (map__load(map)) return 0; - start = dso__first_symbol(map->dso, MAP__FUNCTION); + start = dso__first_symbol(map->dso); for (sym = start; sym; sym = dso__next_symbol(sym)) { if (sym->binding == STB_GLOBAL && diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 1cca0a2fa641..976e658e38dc 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -14,11 +14,12 @@ #include "config.h" #include "util.h" #include <sys/wait.h> +#include <subcmd/exec-cmd.h> #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ - "$CLANG_OPTIONS $KERNEL_INC_OPTIONS " \ + "$CLANG_OPTIONS $KERNEL_INC_OPTIONS $PERF_BPF_INC_OPTIONS " \ "-Wno-unused-value -Wno-pointer-sign " \ "-working-directory $WORKING_DIR " \ "-c \"$CLANG_SOURCE\" -target bpf -O2 -o -" @@ -212,7 +213,7 @@ version_notice(void) " \t\thttp://llvm.org/apt\n\n" " \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n" " \toption in [llvm] section of ~/.perfconfig to:\n\n" -" \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \\\n" +" \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS $PERF_BPF_INC_OPTIONS \\\n" " \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n" " \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n" " \t(Replace /path/to/llc with path to your llc)\n\n" @@ -431,9 +432,11 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, const char *clang_opt = llvm_param.clang_opt; char clang_path[PATH_MAX], abspath[PATH_MAX], nr_cpus_avail_str[64]; char serr[STRERR_BUFSIZE]; - char *kbuild_dir = NULL, *kbuild_include_opts = NULL; + char *kbuild_dir = NULL, *kbuild_include_opts = NULL, + *perf_bpf_include_opts = NULL; const char *template = llvm_param.clang_bpf_cmd_template; - char *command_echo, *command_out; + char *command_echo = NULL, *command_out; + char *perf_include_dir = system_path(PERF_INCLUDE_DIR); if (path[0] != '-' && realpath(path, abspath) == NULL) { err = errno; @@ -471,12 +474,14 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, snprintf(linux_version_code_str, sizeof(linux_version_code_str), "0x%x", kernel_version); - + if (asprintf(&perf_bpf_include_opts, "-I%s/bpf", perf_include_dir) < 0) + goto errout; force_set_env("NR_CPUS", nr_cpus_avail_str); force_set_env("LINUX_VERSION_CODE", linux_version_code_str); force_set_env("CLANG_EXEC", clang_path); force_set_env("CLANG_OPTIONS", clang_opt); force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts); + force_set_env("PERF_BPF_INC_OPTIONS", perf_bpf_include_opts); force_set_env("WORKING_DIR", kbuild_dir ? : "."); /* @@ -512,6 +517,8 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, free(command_out); free(kbuild_dir); free(kbuild_include_opts); + free(perf_bpf_include_opts); + free(perf_include_dir); if (!p_obj_buf) free(obj_buf); @@ -526,6 +533,8 @@ errout: free(kbuild_dir); free(kbuild_include_opts); free(obj_buf); + free(perf_bpf_include_opts); + free(perf_include_dir); if (p_obj_buf) *p_obj_buf = NULL; if (p_obj_buf_sz) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 32d50492505d..e7b4a8b513f2 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -24,6 +24,7 @@ #include "sane_ctype.h" #include <symbol/kallsyms.h> +#include <linux/mman.h> static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock); @@ -81,8 +82,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) machine->kptr_restrict_warned = false; machine->comm_exec = false; machine->kernel_start = 0; - - memset(machine->vmlinux_maps, 0, sizeof(machine->vmlinux_maps)); + machine->vmlinux_map = NULL; machine->root_dir = strdup(root_dir); if (machine->root_dir == NULL) @@ -137,13 +137,11 @@ struct machine *machine__new_kallsyms(void) struct machine *machine = machine__new_host(); /* * FIXME: - * 1) MAP__FUNCTION will go away when we stop loading separate maps for - * functions and data objects. - * 2) We should switch to machine__load_kallsyms(), i.e. not explicitely + * 1) We should switch to machine__load_kallsyms(), i.e. not explicitely * ask for not using the kcore parsing code, once this one is fixed * to create a map per module. */ - if (machine && machine__load_kallsyms(machine, "/proc/kallsyms", MAP__FUNCTION) <= 0) { + if (machine && machine__load_kallsyms(machine, "/proc/kallsyms") <= 0) { machine__delete(machine); machine = NULL; } @@ -673,8 +671,7 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start, if (kmod_path__parse_name(&m, filename)) return NULL; - map = map_groups__find_by_name(&machine->kmaps, MAP__FUNCTION, - m.name); + map = map_groups__find_by_name(&machine->kmaps, m.name); if (map) { /* * If the map's dso is an offline module, give dso__load() @@ -689,7 +686,7 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start, if (dso == NULL) goto out; - map = map__new2(start, dso, MAP__FUNCTION); + map = map__new2(start, dso); if (map == NULL) goto out; @@ -810,8 +807,8 @@ struct process_args { u64 start; }; -static void machine__get_kallsyms_filename(struct machine *machine, char *buf, - size_t bufsz) +void machine__get_kallsyms_filename(struct machine *machine, char *buf, + size_t bufsz) { if (machine__is_default_guest(machine)) scnprintf(buf, bufsz, "%s", symbol_conf.default_guest_kallsyms); @@ -854,65 +851,171 @@ static int machine__get_running_kernel_start(struct machine *machine, return 0; } +int machine__create_extra_kernel_map(struct machine *machine, + struct dso *kernel, + struct extra_kernel_map *xm) +{ + struct kmap *kmap; + struct map *map; + + map = map__new2(xm->start, kernel); + if (!map) + return -1; + + map->end = xm->end; + map->pgoff = xm->pgoff; + + kmap = map__kmap(map); + + kmap->kmaps = &machine->kmaps; + strlcpy(kmap->name, xm->name, KMAP_NAME_LEN); + + map_groups__insert(&machine->kmaps, map); + + pr_debug2("Added extra kernel map %s %" PRIx64 "-%" PRIx64 "\n", + kmap->name, map->start, map->end); + + map__put(map); + + return 0; +} + +static u64 find_entry_trampoline(struct dso *dso) +{ + /* Duplicates are removed so lookup all aliases */ + const char *syms[] = { + "_entry_trampoline", + "__entry_trampoline_start", + "entry_SYSCALL_64_trampoline", + }; + struct symbol *sym = dso__first_symbol(dso); + unsigned int i; + + for (; sym; sym = dso__next_symbol(sym)) { + if (sym->binding != STB_GLOBAL) + continue; + for (i = 0; i < ARRAY_SIZE(syms); i++) { + if (!strcmp(sym->name, syms[i])) + return sym->start; + } + } + + return 0; +} + +/* + * These values can be used for kernels that do not have symbols for the entry + * trampolines in kallsyms. + */ +#define X86_64_CPU_ENTRY_AREA_PER_CPU 0xfffffe0000000000ULL +#define X86_64_CPU_ENTRY_AREA_SIZE 0x2c000 +#define X86_64_ENTRY_TRAMPOLINE 0x6000 + +/* Map x86_64 PTI entry trampolines */ +int machine__map_x86_64_entry_trampolines(struct machine *machine, + struct dso *kernel) +{ + struct map_groups *kmaps = &machine->kmaps; + struct maps *maps = &kmaps->maps; + int nr_cpus_avail, cpu; + bool found = false; + struct map *map; + u64 pgoff; + + /* + * In the vmlinux case, pgoff is a virtual address which must now be + * mapped to a vmlinux offset. + */ + for (map = maps__first(maps); map; map = map__next(map)) { + struct kmap *kmap = __map__kmap(map); + struct map *dest_map; + + if (!kmap || !is_entry_trampoline(kmap->name)) + continue; + + dest_map = map_groups__find(kmaps, map->pgoff); + if (dest_map != map) + map->pgoff = dest_map->map_ip(dest_map, map->pgoff); + found = true; + } + if (found || machine->trampolines_mapped) + return 0; + + pgoff = find_entry_trampoline(kernel); + if (!pgoff) + return 0; + + nr_cpus_avail = machine__nr_cpus_avail(machine); + + /* Add a 1 page map for each CPU's entry trampoline */ + for (cpu = 0; cpu < nr_cpus_avail; cpu++) { + u64 va = X86_64_CPU_ENTRY_AREA_PER_CPU + + cpu * X86_64_CPU_ENTRY_AREA_SIZE + + X86_64_ENTRY_TRAMPOLINE; + struct extra_kernel_map xm = { + .start = va, + .end = va + page_size, + .pgoff = pgoff, + }; + + strlcpy(xm.name, ENTRY_TRAMPOLINE_NAME, KMAP_NAME_LEN); + + if (machine__create_extra_kernel_map(machine, kernel, &xm) < 0) + return -1; + } + + machine->trampolines_mapped = nr_cpus_avail; + + return 0; +} + +int __weak machine__create_extra_kernel_maps(struct machine *machine __maybe_unused, + struct dso *kernel __maybe_unused) +{ + return 0; +} + static int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) { - int type; + struct kmap *kmap; + struct map *map; /* In case of renewal the kernel map, destroy previous one */ machine__destroy_kernel_maps(machine); - for (type = 0; type < MAP__NR_TYPES; ++type) { - struct kmap *kmap; - struct map *map; - - machine->vmlinux_maps[type] = map__new2(0, kernel, type); - if (machine->vmlinux_maps[type] == NULL) - return -1; + machine->vmlinux_map = map__new2(0, kernel); + if (machine->vmlinux_map == NULL) + return -1; - machine->vmlinux_maps[type]->map_ip = - machine->vmlinux_maps[type]->unmap_ip = - identity__map_ip; - map = __machine__kernel_map(machine, type); - kmap = map__kmap(map); - if (!kmap) - return -1; + machine->vmlinux_map->map_ip = machine->vmlinux_map->unmap_ip = identity__map_ip; + map = machine__kernel_map(machine); + kmap = map__kmap(map); + if (!kmap) + return -1; - kmap->kmaps = &machine->kmaps; - map_groups__insert(&machine->kmaps, map); - } + kmap->kmaps = &machine->kmaps; + map_groups__insert(&machine->kmaps, map); return 0; } void machine__destroy_kernel_maps(struct machine *machine) { - int type; - - for (type = 0; type < MAP__NR_TYPES; ++type) { - struct kmap *kmap; - struct map *map = __machine__kernel_map(machine, type); - - if (map == NULL) - continue; + struct kmap *kmap; + struct map *map = machine__kernel_map(machine); - kmap = map__kmap(map); - map_groups__remove(&machine->kmaps, map); - if (kmap && kmap->ref_reloc_sym) { - /* - * ref_reloc_sym is shared among all maps, so free just - * on one of them. - */ - if (type == MAP__FUNCTION) { - zfree((char **)&kmap->ref_reloc_sym->name); - zfree(&kmap->ref_reloc_sym); - } else - kmap->ref_reloc_sym = NULL; - } + if (map == NULL) + return; - map__put(machine->vmlinux_maps[type]); - machine->vmlinux_maps[type] = NULL; + kmap = map__kmap(map); + map_groups__remove(&machine->kmaps, map); + if (kmap && kmap->ref_reloc_sym) { + zfree((char **)&kmap->ref_reloc_sym->name); + zfree(&kmap->ref_reloc_sym); } + + map__zput(machine->vmlinux_map); } int machines__create_guest_kernel_maps(struct machines *machines) @@ -989,32 +1092,31 @@ int machines__create_kernel_maps(struct machines *machines, pid_t pid) return machine__create_kernel_maps(machine); } -int machine__load_kallsyms(struct machine *machine, const char *filename, - enum map_type type) +int machine__load_kallsyms(struct machine *machine, const char *filename) { struct map *map = machine__kernel_map(machine); int ret = __dso__load_kallsyms(map->dso, filename, map, true); if (ret > 0) { - dso__set_loaded(map->dso, type); + dso__set_loaded(map->dso); /* * Since /proc/kallsyms will have multiple sessions for the * kernel, with modules between them, fixup the end of all * sections. */ - __map_groups__fixup_end(&machine->kmaps, type); + map_groups__fixup_end(&machine->kmaps); } return ret; } -int machine__load_vmlinux_path(struct machine *machine, enum map_type type) +int machine__load_vmlinux_path(struct machine *machine) { struct map *map = machine__kernel_map(machine); int ret = dso__load_vmlinux_path(map->dso, map); if (ret > 0) - dso__set_loaded(map->dso, type); + dso__set_loaded(map->dso); return ret; } @@ -1055,10 +1157,9 @@ static bool is_kmod_dso(struct dso *dso) static int map_groups__set_module_path(struct map_groups *mg, const char *path, struct kmod_path *m) { - struct map *map; char *long_name; + struct map *map = map_groups__find_by_name(mg, m->name); - map = map_groups__find_by_name(mg, MAP__FUNCTION, m->name); if (map == NULL) return 0; @@ -1207,19 +1308,14 @@ static int machine__create_modules(struct machine *machine) static void machine__set_kernel_mmap(struct machine *machine, u64 start, u64 end) { - int i; - - for (i = 0; i < MAP__NR_TYPES; i++) { - machine->vmlinux_maps[i]->start = start; - machine->vmlinux_maps[i]->end = end; - - /* - * Be a bit paranoid here, some perf.data file came with - * a zero sized synthesized MMAP event for the kernel. - */ - if (start == 0 && end == 0) - machine->vmlinux_maps[i]->end = ~0ULL; - } + machine->vmlinux_map->start = start; + machine->vmlinux_map->end = end; + /* + * Be a bit paranoid here, some perf.data file came with + * a zero sized synthesized MMAP event for the kernel. + */ + if (start == 0 && end == 0) + machine->vmlinux_map->end = ~0ULL; } int machine__create_kernel_maps(struct machine *machine) @@ -1234,9 +1330,8 @@ int machine__create_kernel_maps(struct machine *machine) return -1; ret = __machine__create_kernel_maps(machine, kernel); - dso__put(kernel); if (ret < 0) - return -1; + goto out_put; if (symbol_conf.use_modules && machine__create_modules(machine) < 0) { if (machine__is_host(machine)) @@ -1249,9 +1344,10 @@ int machine__create_kernel_maps(struct machine *machine) if (!machine__get_running_kernel_start(machine, &name, &addr)) { if (name && - maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, name, addr)) { + map__set_kallsyms_ref_reloc_sym(machine->vmlinux_map, name, addr)) { machine__destroy_kernel_maps(machine); - return -1; + ret = -1; + goto out_put; } /* we have a real start address now, so re-order the kmaps */ @@ -1267,12 +1363,16 @@ int machine__create_kernel_maps(struct machine *machine) map__put(map); } + if (machine__create_extra_kernel_maps(machine, kernel)) + pr_debug("Problems creating extra kernel maps, continuing anyway...\n"); + /* update end address of the kernel map using adjacent module address */ map = map__next(machine__kernel_map(machine)); if (map) machine__set_kernel_mmap(machine, addr, map->start); - - return 0; +out_put: + dso__put(kernel); + return ret; } static bool machine__uses_kcore(struct machine *machine) @@ -1287,6 +1387,32 @@ static bool machine__uses_kcore(struct machine *machine) return false; } +static bool perf_event__is_extra_kernel_mmap(struct machine *machine, + union perf_event *event) +{ + return machine__is(machine, "x86_64") && + is_entry_trampoline(event->mmap.filename); +} + +static int machine__process_extra_kernel_map(struct machine *machine, + union perf_event *event) +{ + struct map *kernel_map = machine__kernel_map(machine); + struct dso *kernel = kernel_map ? kernel_map->dso : NULL; + struct extra_kernel_map xm = { + .start = event->mmap.start, + .end = event->mmap.start + event->mmap.len, + .pgoff = event->mmap.pgoff, + }; + + if (kernel == NULL) + return -1; + + strlcpy(xm.name, event->mmap.filename, KMAP_NAME_LEN); + + return machine__create_extra_kernel_map(machine, kernel, &xm); +} + static int machine__process_kernel_mmap_event(struct machine *machine, union perf_event *event) { @@ -1379,9 +1505,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine, * time /proc/sys/kernel/kptr_restrict was non zero. */ if (event->mmap.pgoff != 0) { - maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, - symbol_name, - event->mmap.pgoff); + map__set_kallsyms_ref_reloc_sym(machine->vmlinux_map, + symbol_name, + event->mmap.pgoff); } if (machine__is_default_guest(machine)) { @@ -1390,6 +1516,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine, */ dso__load(kernel, machine__kernel_map(machine)); } + } else if (perf_event__is_extra_kernel_mmap(machine, event)) { + return machine__process_extra_kernel_map(machine, event); } return 0; out_problem: @@ -1402,7 +1530,6 @@ int machine__process_mmap2_event(struct machine *machine, { struct thread *thread; struct map *map; - enum map_type type; int ret = 0; if (dump_trace) @@ -1421,11 +1548,6 @@ int machine__process_mmap2_event(struct machine *machine, if (thread == NULL) goto out_problem; - if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) - type = MAP__VARIABLE; - else - type = MAP__FUNCTION; - map = map__new(machine, event->mmap2.start, event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, @@ -1433,7 +1555,7 @@ int machine__process_mmap2_event(struct machine *machine, event->mmap2.ino_generation, event->mmap2.prot, event->mmap2.flags, - event->mmap2.filename, type, thread); + event->mmap2.filename, thread); if (map == NULL) goto out_problem_map; @@ -1460,7 +1582,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event { struct thread *thread; struct map *map; - enum map_type type; + u32 prot = 0; int ret = 0; if (dump_trace) @@ -1479,16 +1601,14 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event if (thread == NULL) goto out_problem; - if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) - type = MAP__VARIABLE; - else - type = MAP__FUNCTION; + if (!(event->header.misc & PERF_RECORD_MISC_MMAP_DATA)) + prot = PROT_EXEC; map = map__new(machine, event->mmap.start, event->mmap.len, event->mmap.pgoff, - 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, prot, 0, event->mmap.filename, - type, thread); + thread); if (map == NULL) goto out_problem_map; @@ -1664,7 +1784,7 @@ static void ip__resolve_ams(struct thread *thread, * Thus, we have to try consecutively until we find a match * or else, the symbol is unknown */ - thread__find_cpumode_addr_location(thread, MAP__FUNCTION, ip, &al); + thread__find_cpumode_addr_location(thread, ip, &al); ams->addr = ip; ams->al_addr = al.addr; @@ -1681,15 +1801,7 @@ static void ip__resolve_data(struct thread *thread, memset(&al, 0, sizeof(al)); - thread__find_addr_location(thread, m, MAP__VARIABLE, addr, &al); - if (al.map == NULL) { - /* - * some shared data regions have execute bit set which puts - * their mapping in the MAP__FUNCTION type array. - * Check there as a fallback option before dropping the sample. - */ - thread__find_addr_location(thread, m, MAP__FUNCTION, addr, &al); - } + thread__find_symbol(thread, m, addr, &al); ams->addr = addr; ams->al_addr = al.addr; @@ -1758,8 +1870,7 @@ static int add_callchain_ip(struct thread *thread, al.filtered = 0; al.sym = NULL; if (!cpumode) { - thread__find_cpumode_addr_location(thread, MAP__FUNCTION, - ip, &al); + thread__find_cpumode_addr_location(thread, ip, &al); } else { if (ip >= PERF_CONTEXT_MAX) { switch (ip) { @@ -1784,8 +1895,7 @@ static int add_callchain_ip(struct thread *thread, } return 0; } - thread__find_addr_location(thread, *cpumode, MAP__FUNCTION, - ip, &al); + thread__find_symbol(thread, *cpumode, ip, &al); } if (al.sym != NULL) { @@ -1810,7 +1920,7 @@ static int add_callchain_ip(struct thread *thread, } srcline = callchain_srcline(al.map, al.sym, al.addr); - return callchain_cursor_append(cursor, al.addr, al.map, al.sym, + return callchain_cursor_append(cursor, ip, al.map, al.sym, branch, flags, nr_loop_iter, iter_cycles, branch_from, srcline); } @@ -2342,6 +2452,20 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, return 0; } +/* + * Compares the raw arch string. N.B. see instead perf_env__arch() if a + * normalized arch is needed. + */ +bool machine__is(struct machine *machine, const char *arch) +{ + return machine && !strcmp(perf_env__raw_arch(machine->env), arch); +} + +int machine__nr_cpus_avail(struct machine *machine) +{ + return machine ? perf_env__nr_cpus_avail(machine->env) : 0; +} + int machine__get_kernel_start(struct machine *machine) { struct map *map = machine__kernel_map(machine); @@ -2358,7 +2482,12 @@ int machine__get_kernel_start(struct machine *machine) machine->kernel_start = 1ULL << 63; if (map) { err = map__load(map); - if (!err) + /* + * On x86_64, PTI entry trampolines are less than the + * start of kernel text, but still above 2^63. So leave + * kernel_start = 1ULL << 63 for x86_64. + */ + if (!err && !machine__is(machine, "x86_64")) machine->kernel_start = map->start; } return err; @@ -2373,7 +2502,7 @@ char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, ch { struct machine *machine = vmachine; struct map *map; - struct symbol *sym = map_groups__find_symbol(&machine->kmaps, MAP__FUNCTION, *addrp, &map); + struct symbol *sym = machine__find_kernel_symbol(machine, *addrp, &map); if (sym == NULL) return NULL; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 66cc200ef86f..1de7660d93e9 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -49,13 +49,14 @@ struct machine { struct perf_env *env; struct dsos dsos; struct map_groups kmaps; - struct map *vmlinux_maps[MAP__NR_TYPES]; + struct map *vmlinux_map; u64 kernel_start; pid_t *current_tid; union { /* Tool specific area */ void *priv; u64 db_id; }; + bool trampolines_mapped; }; static inline struct threads *machine__threads(struct machine *machine, pid_t tid) @@ -64,16 +65,22 @@ static inline struct threads *machine__threads(struct machine *machine, pid_t ti return &machine->threads[(unsigned int)tid % THREADS__TABLE_SIZE]; } +/* + * The main kernel (vmlinux) map + */ static inline -struct map *__machine__kernel_map(struct machine *machine, enum map_type type) +struct map *machine__kernel_map(struct machine *machine) { - return machine->vmlinux_maps[type]; + return machine->vmlinux_map; } +/* + * kernel (the one returned by machine__kernel_map()) plus kernel modules maps + */ static inline -struct map *machine__kernel_map(struct machine *machine) +struct maps *machine__kernel_maps(struct machine *machine) { - return __machine__kernel_map(machine, MAP__FUNCTION); + return &machine->kmaps.maps; } int machine__get_kernel_start(struct machine *machine); @@ -182,6 +189,9 @@ static inline bool machine__is_host(struct machine *machine) return machine ? machine->pid == HOST_KERNEL_ID : false; } +bool machine__is(struct machine *machine, const char *arch); +int machine__nr_cpus_avail(struct machine *machine); + struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid); struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid); @@ -190,44 +200,27 @@ struct dso *machine__findnew_dso(struct machine *machine, const char *filename); size_t machine__fprintf(struct machine *machine, FILE *fp); static inline -struct symbol *machine__find_kernel_symbol(struct machine *machine, - enum map_type type, u64 addr, +struct symbol *machine__find_kernel_symbol(struct machine *machine, u64 addr, struct map **mapp) { - return map_groups__find_symbol(&machine->kmaps, type, addr, mapp); + return map_groups__find_symbol(&machine->kmaps, addr, mapp); } static inline struct symbol *machine__find_kernel_symbol_by_name(struct machine *machine, - enum map_type type, const char *name, + const char *name, struct map **mapp) { - return map_groups__find_symbol_by_name(&machine->kmaps, type, name, mapp); -} - -static inline -struct symbol *machine__find_kernel_function(struct machine *machine, u64 addr, - struct map **mapp) -{ - return machine__find_kernel_symbol(machine, MAP__FUNCTION, addr, - mapp); -} - -static inline -struct symbol *machine__find_kernel_function_by_name(struct machine *machine, - const char *name, - struct map **mapp) -{ - return map_groups__find_function_by_name(&machine->kmaps, name, mapp); + return map_groups__find_symbol_by_name(&machine->kmaps, name, mapp); } struct map *machine__findnew_module_map(struct machine *machine, u64 start, const char *filename); int arch__fix_module_text_start(u64 *start, const char *name); -int machine__load_kallsyms(struct machine *machine, const char *filename, - enum map_type type); -int machine__load_vmlinux_path(struct machine *machine, enum map_type type); +int machine__load_kallsyms(struct machine *machine, const char *filename); + +int machine__load_vmlinux_path(struct machine *machine); size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, bool (skip)(struct dso *dso, int parm), int parm); @@ -276,4 +269,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, */ char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp); +void machine__get_kallsyms_filename(struct machine *machine, char *buf, + size_t bufsz); + +int machine__create_extra_kernel_maps(struct machine *machine, + struct dso *kernel); + +/* Kernel-space maps for symbols that are outside the main kernel map and module maps */ +struct extra_kernel_map { + u64 start; + u64 end; + u64 pgoff; + char name[KMAP_NAME_LEN]; +}; + +int machine__create_extra_kernel_map(struct machine *machine, + struct dso *kernel, + struct extra_kernel_map *xm); + +int machine__map_x86_64_entry_trampolines(struct machine *machine, + struct dso *kernel); + #endif /* __PERF_MACHINE_H */ diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 8fe57031e1a8..6ae97eda370b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -22,11 +22,6 @@ static void __maps__insert(struct maps *maps, struct map *map); -const char *map_type__name[MAP__NR_TYPES] = { - [MAP__FUNCTION] = "Functions", - [MAP__VARIABLE] = "Variables", -}; - static inline int is_anon_memory(const char *filename, u32 flags) { return flags & MAP_HUGETLB || @@ -129,10 +124,8 @@ static inline bool replace_android_lib(const char *filename, char *newfilename) return false; } -void map__init(struct map *map, enum map_type type, - u64 start, u64 end, u64 pgoff, struct dso *dso) +void map__init(struct map *map, u64 start, u64 end, u64 pgoff, struct dso *dso) { - map->type = type; map->start = start; map->end = end; map->pgoff = pgoff; @@ -149,7 +142,7 @@ void map__init(struct map *map, enum map_type type, struct map *map__new(struct machine *machine, u64 start, u64 len, u64 pgoff, u32 d_maj, u32 d_min, u64 ino, u64 ino_gen, u32 prot, u32 flags, char *filename, - enum map_type type, struct thread *thread) + struct thread *thread) { struct map *map = malloc(sizeof(*map)); struct nsinfo *nsi = NULL; @@ -173,7 +166,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, map->flags = flags; nsi = nsinfo__get(thread->nsinfo); - if ((anon || no_dso) && nsi && type == MAP__FUNCTION) { + if ((anon || no_dso) && nsi && (prot & PROT_EXEC)) { snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", nsi->pid); filename = newfilename; @@ -203,7 +196,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, if (dso == NULL) goto out_delete; - map__init(map, type, start, start + len, pgoff, dso); + map__init(map, start, start + len, pgoff, dso); if (anon || no_dso) { map->map_ip = map->unmap_ip = identity__map_ip; @@ -213,8 +206,8 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, * functions still return NULL, and we avoid the * unnecessary map__load warning. */ - if (type != MAP__FUNCTION) - dso__set_loaded(dso, map->type); + if (!(prot & PROT_EXEC)) + dso__set_loaded(dso); } dso->nsinfo = nsi; dso__put(dso); @@ -231,7 +224,7 @@ out_delete: * they are loaded) and for vmlinux, where only after we load all the * symbols we'll know where it starts and ends. */ -struct map *map__new2(u64 start, struct dso *dso, enum map_type type) +struct map *map__new2(u64 start, struct dso *dso) { struct map *map = calloc(1, (sizeof(*map) + (dso->kernel ? sizeof(struct kmap) : 0))); @@ -239,7 +232,7 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type) /* * ->end will be filled after we load all the symbols */ - map__init(map, type, start, 0, 0, dso); + map__init(map, start, 0, 0, dso); } return map; @@ -256,7 +249,19 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type) */ bool __map__is_kernel(const struct map *map) { - return __machine__kernel_map(map->groups->machine, map->type) == map; + return machine__kernel_map(map->groups->machine) == map; +} + +bool __map__is_extra_kernel_map(const struct map *map) +{ + struct kmap *kmap = __map__kmap((struct map *)map); + + return kmap && kmap->name[0]; +} + +bool map__has_symbols(const struct map *map) +{ + return dso__has_symbols(map->dso); } static void map__exit(struct map *map) @@ -279,7 +284,7 @@ void map__put(struct map *map) void map__fixup_start(struct map *map) { - struct rb_root *symbols = &map->dso->symbols[map->type]; + struct rb_root *symbols = &map->dso->symbols; struct rb_node *nd = rb_first(symbols); if (nd != NULL) { struct symbol *sym = rb_entry(nd, struct symbol, rb_node); @@ -289,7 +294,7 @@ void map__fixup_start(struct map *map) void map__fixup_end(struct map *map) { - struct rb_root *symbols = &map->dso->symbols[map->type]; + struct rb_root *symbols = &map->dso->symbols; struct rb_node *nd = rb_last(symbols); if (nd != NULL) { struct symbol *sym = rb_entry(nd, struct symbol, rb_node); @@ -304,7 +309,7 @@ int map__load(struct map *map) const char *name = map->dso->long_name; int nr; - if (dso__loaded(map->dso, map->type)) + if (dso__loaded(map->dso)) return 0; nr = dso__load(map->dso, map); @@ -348,7 +353,7 @@ struct symbol *map__find_symbol(struct map *map, u64 addr) if (map__load(map) < 0) return NULL; - return dso__find_symbol(map->dso, map->type, addr); + return dso__find_symbol(map->dso, addr); } struct symbol *map__find_symbol_by_name(struct map *map, const char *name) @@ -356,10 +361,10 @@ struct symbol *map__find_symbol_by_name(struct map *map, const char *name) if (map__load(map) < 0) return NULL; - if (!dso__sorted_by_name(map->dso, map->type)) - dso__sort_by_name(map->dso, map->type); + if (!dso__sorted_by_name(map->dso)) + dso__sort_by_name(map->dso); - return dso__find_symbol_by_name(map->dso, map->type, name); + return dso__find_symbol_by_name(map->dso, name); } struct map *map__clone(struct map *from) @@ -494,10 +499,7 @@ static void maps__init(struct maps *maps) void map_groups__init(struct map_groups *mg, struct machine *machine) { - int i; - for (i = 0; i < MAP__NR_TYPES; ++i) { - maps__init(&mg->maps[i]); - } + maps__init(&mg->maps); mg->machine = machine; refcount_set(&mg->refcnt, 1); } @@ -525,22 +527,12 @@ static void maps__exit(struct maps *maps) void map_groups__exit(struct map_groups *mg) { - int i; - - for (i = 0; i < MAP__NR_TYPES; ++i) - maps__exit(&mg->maps[i]); + maps__exit(&mg->maps); } bool map_groups__empty(struct map_groups *mg) { - int i; - - for (i = 0; i < MAP__NR_TYPES; ++i) { - if (maps__first(&mg->maps[i])) - return false; - } - - return true; + return !maps__first(&mg->maps); } struct map_groups *map_groups__new(struct machine *machine) @@ -566,10 +558,9 @@ void map_groups__put(struct map_groups *mg) } struct symbol *map_groups__find_symbol(struct map_groups *mg, - enum map_type type, u64 addr, - struct map **mapp) + u64 addr, struct map **mapp) { - struct map *map = map_groups__find(mg, type, addr); + struct map *map = map_groups__find(mg, addr); /* Ensure map is loaded before using map->map_ip */ if (map != NULL && map__load(map) >= 0) { @@ -608,13 +599,10 @@ out: } struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, - enum map_type type, const char *name, struct map **mapp) { - struct symbol *sym = maps__find_symbol_by_name(&mg->maps[type], name, mapp); - - return sym; + return maps__find_symbol_by_name(&mg->maps, name, mapp); } int map_groups__find_ams(struct addr_map_symbol *ams) @@ -622,8 +610,7 @@ int map_groups__find_ams(struct addr_map_symbol *ams) if (ams->addr < ams->map->start || ams->addr >= ams->map->end) { if (ams->map->groups == NULL) return -1; - ams->map = map_groups__find(ams->map->groups, ams->map->type, - ams->addr); + ams->map = map_groups__find(ams->map->groups, ams->addr); if (ams->map == NULL) return -1; } @@ -646,7 +633,7 @@ static size_t maps__fprintf(struct maps *maps, FILE *fp) printed += fprintf(fp, "Map:"); printed += map__fprintf(pos, fp); if (verbose > 2) { - printed += dso__fprintf(pos->dso, pos->type, fp); + printed += dso__fprintf(pos->dso, fp); printed += fprintf(fp, "--\n"); } } @@ -656,24 +643,14 @@ static size_t maps__fprintf(struct maps *maps, FILE *fp) return printed; } -size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, - FILE *fp) -{ - size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); - return printed += maps__fprintf(&mg->maps[type], fp); -} - size_t map_groups__fprintf(struct map_groups *mg, FILE *fp) { - size_t printed = 0, i; - for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_maps(mg, i, fp); - return printed; + return maps__fprintf(&mg->maps, fp); } static void __map_groups__insert(struct map_groups *mg, struct map *map) { - __maps__insert(&mg->maps[map->type], map); + __maps__insert(&mg->maps, map); map->groups = mg; } @@ -758,19 +735,18 @@ out: int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, FILE *fp) { - return maps__fixup_overlappings(&mg->maps[map->type], map, fp); + return maps__fixup_overlappings(&mg->maps, map, fp); } /* * XXX This should not really _copy_ te maps, but refcount them. */ -int map_groups__clone(struct thread *thread, - struct map_groups *parent, enum map_type type) +int map_groups__clone(struct thread *thread, struct map_groups *parent) { struct map_groups *mg = thread->mg; int err = -ENOMEM; struct map *map; - struct maps *maps = &parent->maps[type]; + struct maps *maps = &parent->maps; down_read(&maps->lock); @@ -877,15 +853,22 @@ struct map *map__next(struct map *map) return NULL; } -struct kmap *map__kmap(struct map *map) +struct kmap *__map__kmap(struct map *map) { - if (!map->dso || !map->dso->kernel) { - pr_err("Internal error: map__kmap with a non-kernel map\n"); + if (!map->dso || !map->dso->kernel) return NULL; - } return (struct kmap *)(map + 1); } +struct kmap *map__kmap(struct map *map) +{ + struct kmap *kmap = __map__kmap(map); + + if (!kmap) + pr_err("Internal error: map__kmap with a non-kernel map\n"); + return kmap; +} + struct map_groups *map__kmaps(struct map *map) { struct kmap *kmap = map__kmap(map); diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 0e9bbe01b0ab..97e2a063bd65 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -8,19 +8,11 @@ #include <linux/rbtree.h> #include <pthread.h> #include <stdio.h> +#include <string.h> #include <stdbool.h> #include <linux/types.h> #include "rwsem.h" -enum map_type { - MAP__FUNCTION = 0, - MAP__VARIABLE, -}; - -#define MAP__NR_TYPES (MAP__VARIABLE + 1) - -extern const char *map_type__name[MAP__NR_TYPES]; - struct dso; struct ip_callchain; struct ref_reloc_sym; @@ -35,7 +27,6 @@ struct map { }; u64 start; u64 end; - u8 /* enum map_type */ type; bool erange_warned; u32 priv; u32 prot; @@ -56,9 +47,12 @@ struct map { refcount_t refcnt; }; +#define KMAP_NAME_LEN 256 + struct kmap { struct ref_reloc_sym *ref_reloc_sym; struct map_groups *kmaps; + char name[KMAP_NAME_LEN]; }; struct maps { @@ -67,7 +61,7 @@ struct maps { }; struct map_groups { - struct maps maps[MAP__NR_TYPES]; + struct maps maps; struct machine *machine; refcount_t refcnt; }; @@ -85,6 +79,7 @@ static inline struct map_groups *map_groups__get(struct map_groups *mg) void map_groups__put(struct map_groups *mg); +struct kmap *__map__kmap(struct map *map); struct kmap *map__kmap(struct map *map); struct map_groups *map__kmaps(struct map *map); @@ -125,7 +120,7 @@ struct thread; * Note: caller must ensure map->dso is not NULL (map is loaded). */ #define map__for_each_symbol(map, pos, n) \ - dso__for_each_symbol(map->dso, pos, n, map->type) + dso__for_each_symbol(map->dso, pos, n) /* map__for_each_symbol_with_name - iterate over the symbols in the given map * that have the given name @@ -144,13 +139,13 @@ struct thread; #define map__for_each_symbol_by_name(map, sym_name, pos) \ __map__for_each_symbol_by_name(map, sym_name, (pos)) -void map__init(struct map *map, enum map_type type, +void map__init(struct map *map, u64 start, u64 end, u64 pgoff, struct dso *dso); struct map *map__new(struct machine *machine, u64 start, u64 len, u64 pgoff, u32 d_maj, u32 d_min, u64 ino, u64 ino_gen, u32 prot, u32 flags, - char *filename, enum map_type type, struct thread *thread); -struct map *map__new2(u64 start, struct dso *dso, enum map_type type); + char *filename, struct thread *thread); +struct map *map__new2(u64 start, struct dso *dso); void map__delete(struct map *map); struct map *map__clone(struct map *map); @@ -185,8 +180,6 @@ void map__fixup_end(struct map *map); void map__reloc_vmlinux(struct map *map); -size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, - FILE *fp); void maps__insert(struct maps *maps, struct map *map); void maps__remove(struct maps *maps, struct map *map); struct map *maps__find(struct maps *maps, u64 addr); @@ -197,34 +190,29 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, void map_groups__init(struct map_groups *mg, struct machine *machine); void map_groups__exit(struct map_groups *mg); int map_groups__clone(struct thread *thread, - struct map_groups *parent, enum map_type type); + struct map_groups *parent); size_t map_groups__fprintf(struct map_groups *mg, FILE *fp); -int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, - u64 addr); +int map__set_kallsyms_ref_reloc_sym(struct map *map, const char *symbol_name, + u64 addr); static inline void map_groups__insert(struct map_groups *mg, struct map *map) { - maps__insert(&mg->maps[map->type], map); + maps__insert(&mg->maps, map); map->groups = mg; } static inline void map_groups__remove(struct map_groups *mg, struct map *map) { - maps__remove(&mg->maps[map->type], map); + maps__remove(&mg->maps, map); } -static inline struct map *map_groups__find(struct map_groups *mg, - enum map_type type, u64 addr) +static inline struct map *map_groups__find(struct map_groups *mg, u64 addr) { - return maps__find(&mg->maps[type], addr); + return maps__find(&mg->maps, addr); } -static inline struct map *map_groups__first(struct map_groups *mg, - enum map_type type) -{ - return maps__first(&mg->maps[type]); -} +struct map *map_groups__first(struct map_groups *mg); static inline struct map *map_groups__next(struct map *map) { @@ -232,11 +220,9 @@ static inline struct map *map_groups__next(struct map *map) } struct symbol *map_groups__find_symbol(struct map_groups *mg, - enum map_type type, u64 addr, - struct map **mapp); + u64 addr, struct map **mapp); struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, - enum map_type type, const char *name, struct map **mapp); @@ -244,24 +230,26 @@ struct addr_map_symbol; int map_groups__find_ams(struct addr_map_symbol *ams); -static inline -struct symbol *map_groups__find_function_by_name(struct map_groups *mg, - const char *name, struct map **mapp) -{ - return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp); -} - int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, FILE *fp); -struct map *map_groups__find_by_name(struct map_groups *mg, - enum map_type type, const char *name); +struct map *map_groups__find_by_name(struct map_groups *mg, const char *name); bool __map__is_kernel(const struct map *map); +bool __map__is_extra_kernel_map(const struct map *map); static inline bool __map__is_kmodule(const struct map *map) { - return !__map__is_kernel(map); + return !__map__is_kernel(map) && !__map__is_extra_kernel_map(map); +} + +bool map__has_symbols(const struct map *map); + +#define ENTRY_TRAMPOLINE_NAME "__entry_SYSCALL_64_trampoline" + +static inline bool is_entry_trampoline(const char *name) +{ + return !strcmp(name, ENTRY_TRAMPOLINE_NAME); } #endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 2fb0272146d8..15eec49e71a1 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -156,13 +156,12 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = { (strcmp(sys_dirent->d_name, ".")) && \ (strcmp(sys_dirent->d_name, ".."))) -static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) +static int tp_event_has_id(const char *dir_path, struct dirent *evt_dir) { char evt_path[MAXPATHLEN]; int fd; - snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", tracing_events_path, - sys_dir->d_name, evt_dir->d_name); + snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path, evt_dir->d_name); fd = open(evt_path, O_RDONLY); if (fd < 0) return -EINVAL; @@ -171,12 +170,12 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) return 0; } -#define for_each_event(sys_dirent, evt_dir, evt_dirent) \ +#define for_each_event(dir_path, evt_dir, evt_dirent) \ while ((evt_dirent = readdir(evt_dir)) != NULL) \ if (evt_dirent->d_type == DT_DIR && \ (strcmp(evt_dirent->d_name, ".")) && \ (strcmp(evt_dirent->d_name, "..")) && \ - (!tp_event_has_id(sys_dirent, evt_dirent))) + (!tp_event_has_id(dir_path, evt_dirent))) #define MAX_EVENT_LENGTH 512 @@ -190,21 +189,21 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) int fd; u64 id; char evt_path[MAXPATHLEN]; - char dir_path[MAXPATHLEN]; + char *dir_path; - sys_dir = opendir(tracing_events_path); + sys_dir = tracing_events__opendir(); if (!sys_dir) return NULL; for_each_subsystem(sys_dir, sys_dirent) { - - snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, - sys_dirent->d_name); + dir_path = get_events_file(sys_dirent->d_name); + if (!dir_path) + continue; evt_dir = opendir(dir_path); if (!evt_dir) - continue; + goto next; - for_each_event(sys_dirent, evt_dir, evt_dirent) { + for_each_event(dir_path, evt_dir, evt_dirent) { scnprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path, evt_dirent->d_name); @@ -218,6 +217,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) close(fd); id = atoll(id_buf); if (id == config) { + put_events_file(dir_path); closedir(evt_dir); closedir(sys_dir); path = zalloc(sizeof(*path)); @@ -242,6 +242,8 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) } } closedir(evt_dir); +next: + put_events_file(dir_path); } closedir(sys_dir); @@ -512,14 +514,19 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, struct parse_events_error *err, struct list_head *head_config) { - char evt_path[MAXPATHLEN]; + char *evt_path; struct dirent *evt_ent; DIR *evt_dir; int ret = 0, found = 0; - snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); + evt_path = get_events_file(sys_name); + if (!evt_path) { + tracepoint_error(err, errno, sys_name, evt_name); + return -1; + } evt_dir = opendir(evt_path); if (!evt_dir) { + put_events_file(evt_path); tracepoint_error(err, errno, sys_name, evt_name); return -1; } @@ -545,6 +552,7 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, ret = -1; } + put_events_file(evt_path); closedir(evt_dir); return ret; } @@ -570,7 +578,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, DIR *events_dir; int ret = 0; - events_dir = opendir(tracing_events_path); + events_dir = tracing_events__opendir(); if (!events_dir) { tracepoint_error(err, errno, sys_name, evt_name); return -1; @@ -1219,13 +1227,16 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, int parse_events_add_pmu(struct parse_events_state *parse_state, struct list_head *list, char *name, - struct list_head *head_config, bool auto_merge_stats) + struct list_head *head_config, + bool auto_merge_stats, + bool use_alias) { struct perf_event_attr attr; struct perf_pmu_info info; struct perf_pmu *pmu; struct perf_evsel *evsel; struct parse_events_error *err = parse_state->error; + bool use_uncore_alias; LIST_HEAD(config_terms); pmu = perf_pmu__find(name); @@ -1244,11 +1255,14 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, memset(&attr, 0, sizeof(attr)); } + use_uncore_alias = (pmu->is_uncore && use_alias); + if (!head_config) { attr.type = pmu->type; evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL, auto_merge_stats); if (evsel) { evsel->pmu_name = name; + evsel->use_uncore_alias = use_uncore_alias; return 0; } else { return -ENOMEM; @@ -1282,6 +1296,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, evsel->metric_expr = info.metric_expr; evsel->metric_name = info.metric_name; evsel->pmu_name = name; + evsel->use_uncore_alias = use_uncore_alias; } return evsel ? 0 : -ENOMEM; @@ -1317,7 +1332,8 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, list_add_tail(&term->list, head); if (!parse_events_add_pmu(parse_state, list, - pmu->name, head, true)) { + pmu->name, head, + true, true)) { pr_debug("%s -> %s/%s/\n", str, pmu->name, alias->str); ok++; @@ -1339,7 +1355,120 @@ int parse_events__modifier_group(struct list_head *list, return parse_events__modifier_event(list, event_mod, true); } -void parse_events__set_leader(char *name, struct list_head *list) +/* + * Check if the two uncore PMUs are from the same uncore block + * The format of the uncore PMU name is uncore_#blockname_#pmuidx + */ +static bool is_same_uncore_block(const char *pmu_name_a, const char *pmu_name_b) +{ + char *end_a, *end_b; + + end_a = strrchr(pmu_name_a, '_'); + end_b = strrchr(pmu_name_b, '_'); + + if (!end_a || !end_b) + return false; + + if ((end_a - pmu_name_a) != (end_b - pmu_name_b)) + return false; + + return (strncmp(pmu_name_a, pmu_name_b, end_a - pmu_name_a) == 0); +} + +static int +parse_events__set_leader_for_uncore_aliase(char *name, struct list_head *list, + struct parse_events_state *parse_state) +{ + struct perf_evsel *evsel, *leader; + uintptr_t *leaders; + bool is_leader = true; + int i, nr_pmu = 0, total_members, ret = 0; + + leader = list_first_entry(list, struct perf_evsel, node); + evsel = list_last_entry(list, struct perf_evsel, node); + total_members = evsel->idx - leader->idx + 1; + + leaders = calloc(total_members, sizeof(uintptr_t)); + if (WARN_ON(!leaders)) + return 0; + + /* + * Going through the whole group and doing sanity check. + * All members must use alias, and be from the same uncore block. + * Also, storing the leader events in an array. + */ + __evlist__for_each_entry(list, evsel) { + + /* Only split the uncore group which members use alias */ + if (!evsel->use_uncore_alias) + goto out; + + /* The events must be from the same uncore block */ + if (!is_same_uncore_block(leader->pmu_name, evsel->pmu_name)) + goto out; + + if (!is_leader) + continue; + /* + * If the event's PMU name starts to repeat, it must be a new + * event. That can be used to distinguish the leader from + * other members, even they have the same event name. + */ + if ((leader != evsel) && (leader->pmu_name == evsel->pmu_name)) { + is_leader = false; + continue; + } + /* The name is always alias name */ + WARN_ON(strcmp(leader->name, evsel->name)); + + /* Store the leader event for each PMU */ + leaders[nr_pmu++] = (uintptr_t) evsel; + } + + /* only one event alias */ + if (nr_pmu == total_members) { + parse_state->nr_groups--; + goto handled; + } + + /* + * An uncore event alias is a joint name which means the same event + * runs on all PMUs of a block. + * Perf doesn't support mixed events from different PMUs in the same + * group. The big group has to be split into multiple small groups + * which only include the events from the same PMU. + * + * Here the uncore event aliases must be from the same uncore block. + * The number of PMUs must be same for each alias. The number of new + * small groups equals to the number of PMUs. + * Setting the leader event for corresponding members in each group. + */ + i = 0; + __evlist__for_each_entry(list, evsel) { + if (i >= nr_pmu) + i = 0; + evsel->leader = (struct perf_evsel *) leaders[i++]; + } + + /* The number of members and group name are same for each group */ + for (i = 0; i < nr_pmu; i++) { + evsel = (struct perf_evsel *) leaders[i]; + evsel->nr_members = total_members / nr_pmu; + evsel->group_name = name ? strdup(name) : NULL; + } + + /* Take the new small groups into account */ + parse_state->nr_groups += nr_pmu - 1; + +handled: + ret = 1; +out: + free(leaders); + return ret; +} + +void parse_events__set_leader(char *name, struct list_head *list, + struct parse_events_state *parse_state) { struct perf_evsel *leader; @@ -1348,6 +1477,9 @@ void parse_events__set_leader(char *name, struct list_head *list) return; } + if (parse_events__set_leader_for_uncore_aliase(name, list, parse_state)) + return; + __perf_evlist__set_leader(list); leader = list_entry(list->next, struct perf_evsel, node); leader->group_name = name ? strdup(name) : NULL; @@ -1715,7 +1847,7 @@ int parse_events(struct perf_evlist *evlist, const char *str, struct perf_evsel *last; if (list_empty(&parse_state.list)) { - WARN_ONCE(true, "WARNING: event parser found nothing"); + WARN_ONCE(true, "WARNING: event parser found nothing\n"); return -1; } @@ -1968,13 +2100,13 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, DIR *sys_dir, *evt_dir; struct dirent *sys_dirent, *evt_dirent; char evt_path[MAXPATHLEN]; - char dir_path[MAXPATHLEN]; + char *dir_path; char **evt_list = NULL; unsigned int evt_i = 0, evt_num = 0; bool evt_num_known = false; restart: - sys_dir = opendir(tracing_events_path); + sys_dir = tracing_events__opendir(); if (!sys_dir) return; @@ -1989,13 +2121,14 @@ restart: !strglobmatch(sys_dirent->d_name, subsys_glob)) continue; - snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, - sys_dirent->d_name); + dir_path = get_events_file(sys_dirent->d_name); + if (!dir_path) + continue; evt_dir = opendir(dir_path); if (!evt_dir) - continue; + goto next; - for_each_event(sys_dirent, evt_dir, evt_dirent) { + for_each_event(dir_path, evt_dir, evt_dirent) { if (event_glob != NULL && !strglobmatch(evt_dirent->d_name, event_glob)) continue; @@ -2009,11 +2142,15 @@ restart: sys_dirent->d_name, evt_dirent->d_name); evt_list[evt_i] = strdup(evt_path); - if (evt_list[evt_i] == NULL) + if (evt_list[evt_i] == NULL) { + put_events_file(dir_path); goto out_close_evt_dir; + } evt_i++; } closedir(evt_dir); +next: + put_events_file(dir_path); } closedir(sys_dir); @@ -2061,21 +2198,21 @@ int is_valid_tracepoint(const char *event_string) DIR *sys_dir, *evt_dir; struct dirent *sys_dirent, *evt_dirent; char evt_path[MAXPATHLEN]; - char dir_path[MAXPATHLEN]; + char *dir_path; - sys_dir = opendir(tracing_events_path); + sys_dir = tracing_events__opendir(); if (!sys_dir) return 0; for_each_subsystem(sys_dir, sys_dirent) { - - snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, - sys_dirent->d_name); + dir_path = get_events_file(sys_dirent->d_name); + if (!dir_path) + continue; evt_dir = opendir(dir_path); if (!evt_dir) - continue; + goto next; - for_each_event(sys_dirent, evt_dir, evt_dirent) { + for_each_event(dir_path, evt_dir, evt_dirent) { snprintf(evt_path, MAXPATHLEN, "%s:%s", sys_dirent->d_name, evt_dirent->d_name); if (!strcmp(evt_path, event_string)) { @@ -2085,6 +2222,8 @@ int is_valid_tracepoint(const char *event_string) } } closedir(evt_dir); +next: + put_events_file(dir_path); } closedir(sys_dir); return 0; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 5015cfd58277..4473dac27aee 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -167,7 +167,9 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, void *ptr, char *type, u64 len); int parse_events_add_pmu(struct parse_events_state *parse_state, struct list_head *list, char *name, - struct list_head *head_config, bool auto_merge_stats); + struct list_head *head_config, + bool auto_merge_stats, + bool use_alias); int parse_events_multi_pmu_add(struct parse_events_state *parse_state, char *str, @@ -178,7 +180,8 @@ int parse_events_copy_term_list(struct list_head *old, 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__set_leader(char *name, struct list_head *list, + struct parse_events_state *parse_state); void parse_events_update_lists(struct list_head *list_event, struct list_head *list_all); void parse_events_evlist_error(struct parse_events_state *parse_state, diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index d14464c42714..e37608a87dba 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -161,7 +161,7 @@ PE_NAME '{' events '}' struct list_head *list = $3; inc_group_count(list, _parse_state); - parse_events__set_leader($1, list); + parse_events__set_leader($1, list, _parse_state); $$ = list; } | @@ -170,7 +170,7 @@ PE_NAME '{' events '}' struct list_head *list = $2; inc_group_count(list, _parse_state); - parse_events__set_leader(NULL, list); + parse_events__set_leader(NULL, list, _parse_state); $$ = list; } @@ -224,15 +224,15 @@ event_def: event_pmu | event_bpf_file event_pmu: -PE_NAME '/' event_config '/' +PE_NAME opt_event_config { struct list_head *list, *orig_terms, *terms; - if (parse_events_copy_term_list($3, &orig_terms)) + if (parse_events_copy_term_list($2, &orig_terms)) YYABORT; ALLOC_LIST(list); - if (parse_events_add_pmu(_parse_state, list, $1, $3, false)) { + if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) { struct perf_pmu *pmu = NULL; int ok = 0; char *pattern; @@ -251,7 +251,7 @@ PE_NAME '/' event_config '/' free(pattern); YYABORT; } - if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true)) + if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false)) ok++; parse_events_terms__delete(terms); } @@ -262,7 +262,7 @@ PE_NAME '/' event_config '/' if (!ok) YYABORT; } - parse_events_terms__delete($3); + parse_events_terms__delete($2); parse_events_terms__delete(orig_terms); $$ = list; } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index e1dbc9821617..3094f11e7d81 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -111,17 +111,6 @@ void exit_probe_symbol_maps(void) symbol__exit(); } -static struct symbol *__find_kernel_function_by_name(const char *name, - struct map **mapp) -{ - return machine__find_kernel_function_by_name(host_machine, name, mapp); -} - -static struct symbol *__find_kernel_function(u64 addr, struct map **mapp) -{ - return machine__find_kernel_function(host_machine, addr, mapp); -} - static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) { /* kmap->ref_reloc_sym should be set if host_machine is initialized */ @@ -149,7 +138,7 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr, if (reloc_sym && strcmp(name, reloc_sym->name) == 0) *addr = (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; else { - sym = __find_kernel_function_by_name(name, &map); + sym = machine__find_kernel_symbol_by_name(host_machine, name, &map); if (!sym) return -ENOENT; *addr = map->unmap_ip(map, sym->start) - @@ -161,8 +150,7 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr, static struct map *kernel_get_module_map(const char *module) { - struct map_groups *grp = &host_machine->kmaps; - struct maps *maps = &grp->maps[MAP__FUNCTION]; + struct maps *maps = machine__kernel_maps(host_machine); struct map *pos; /* A file path -- this is an offline module */ @@ -341,7 +329,7 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso) char module_name[128]; snprintf(module_name, sizeof(module_name), "[%s]", module); - map = map_groups__find_by_name(&host_machine->kmaps, MAP__FUNCTION, module_name); + map = map_groups__find_by_name(&host_machine->kmaps, module_name); if (map) { dso = map->dso; goto found; @@ -2098,7 +2086,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp, } if (addr) { addr += tp->offset; - sym = __find_kernel_function(addr, &map); + sym = machine__find_kernel_symbol(host_machine, addr, &map); } } @@ -3504,19 +3492,18 @@ int show_available_funcs(const char *target, struct nsinfo *nsi, (target) ? : "kernel"); goto end; } - if (!dso__sorted_by_name(map->dso, map->type)) - dso__sort_by_name(map->dso, map->type); + if (!dso__sorted_by_name(map->dso)) + dso__sort_by_name(map->dso); /* Show all (filtered) symbols */ setup_pager(); - for (nd = rb_first(&map->dso->symbol_names[map->type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&map->dso->symbol_names); nd; nd = rb_next(nd)) { struct symbol_name_rb_node *pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); if (strfilter__compare(_filter, pos->sym.name)) printf("%s\n", pos->sym.name); - } - + } end: map__put(map); exit_probe_symbol_maps(); diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 4ae1123c6794..b76088fadf3d 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -84,8 +84,7 @@ int open_trace_file(const char *trace_file, bool readwrite) char buf[PATH_MAX]; int ret; - ret = e_snprintf(buf, PATH_MAX, "%s/%s", - tracing_path, trace_file); + ret = e_snprintf(buf, PATH_MAX, "%s/%s", tracing_path_mount(), trace_file); if (ret >= 0) { pr_debug("Opening %s write=%d\n", buf, readwrite); if (readwrite && !probe_event_dry_run) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 10dd5fce082b..7f8afacd08ee 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -531,6 +531,8 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, PyLong_FromUnsignedLongLong(sample->period)); pydict_set_item_string_decref(dict_sample, "phys_addr", PyLong_FromUnsignedLongLong(sample->phys_addr)); + pydict_set_item_string_decref(dict_sample, "addr", + PyLong_FromUnsignedLongLong(sample->addr)); set_sample_read_in_dict(dict_sample, sample, evsel); pydict_set_item_string_decref(dict, "sample", dict_sample); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f4a7a437ee87..b998bb475589 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1973,12 +1973,11 @@ bool perf_session__has_traces(struct perf_session *session, const char *msg) return false; } -int maps__set_kallsyms_ref_reloc_sym(struct map **maps, - const char *symbol_name, u64 addr) +int map__set_kallsyms_ref_reloc_sym(struct map *map, const char *symbol_name, u64 addr) { char *bracket; - int i; struct ref_reloc_sym *ref; + struct kmap *kmap; ref = zalloc(sizeof(struct ref_reloc_sym)); if (ref == NULL) @@ -1996,13 +1995,9 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps, ref->addr = addr; - for (i = 0; i < MAP__NR_TYPES; ++i) { - struct kmap *kmap = map__kmap(maps[i]); - - if (!kmap) - continue; + kmap = map__kmap(map); + if (kmap) kmap->ref_reloc_sym = ref; - } return 0; } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 26a68dfd8a4f..4058ade352a5 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -2,7 +2,7 @@ #include <errno.h> #include <inttypes.h> #include <regex.h> -#include <sys/mman.h> +#include <linux/mman.h> #include "sort.h" #include "hist.h" #include "comm.h" @@ -282,7 +282,7 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); if (sym && map) { - if (map->type == MAP__VARIABLE) { + if (sym->type == STT_OBJECT) { ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", ip - map->unmap_ip(map, sym->start)); @@ -1211,7 +1211,7 @@ static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, /* print [s] for shared data mmaps */ if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && - map && (map->type == MAP__VARIABLE) && + map && !(map->prot & PROT_EXEC) && (map->flags & MAP_SHARED) && (map->maj || map->min || map->ino || map->ino_generation)) @@ -2582,7 +2582,7 @@ int sort_dimension__add(struct perf_hpp_list *list, const char *tok, if (sort__mode != SORT_MODE__MEMORY) return -EINVAL; - if (sd->entry == &sort_mem_dcacheline && cacheline_size == 0) + if (sd->entry == &sort_mem_dcacheline && cacheline_size() == 0) return -EINVAL; if (sd->entry == &sort_mem_daddr_sym) @@ -2628,7 +2628,7 @@ static int setup_sort_list(struct perf_hpp_list *list, char *str, if (*tok) { ret = sort_dimension__add(list, tok, evlist, level); if (ret == -EINVAL) { - if (!cacheline_size && !strncasecmp(tok, "dcacheline", strlen(tok))) + if (!cacheline_size() && !strncasecmp(tok, "dcacheline", strlen(tok))) pr_err("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system"); else pr_err("Invalid --sort key: `%s'", tok); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 035b62e2c60b..9e6896293bbd 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -186,13 +186,13 @@ static inline float hist_entry__get_percent_limit(struct hist_entry *he) static inline u64 cl_address(u64 address) { /* return the cacheline of the address */ - return (address & ~(cacheline_size - 1)); + return (address & ~(cacheline_size() - 1)); } static inline u64 cl_offset(u64 address) { /* return the cacheline of the address */ - return (address & (cacheline_size - 1)); + return (address & (cacheline_size() - 1)); } enum sort_mode { diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 3c21fd059b64..09d6746e6ec8 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -103,6 +103,7 @@ static struct symbol *new_inline_sym(struct dso *dso, inline_sym = symbol__new(base_sym ? base_sym->start : 0, base_sym ? base_sym->end : 0, base_sym ? base_sym->binding : 0, + base_sym ? base_sym->type : 0, funcname); if (inline_sym) inline_sym->inlined = 1; diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 8f56ba4fd258..36efb986f7fc 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -7,8 +7,7 @@ #include "xyarray.h" #include "rblist.h" -struct stats -{ +struct stats { double n, mean, M2; u64 max, min; }; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 2de770511e70..29770ea61768 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -114,16 +114,9 @@ static inline int elf_sym__is_label(const GElf_Sym *sym) sym->st_shndx != SHN_ABS; } -static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type) +static bool elf_sym__filter(GElf_Sym *sym) { - switch (type) { - case MAP__FUNCTION: - return elf_sym__is_function(sym); - case MAP__VARIABLE: - return elf_sym__is_object(sym); - default: - return false; - } + return elf_sym__is_function(sym) || elf_sym__is_object(sym); } static inline const char *elf_sym__name(const GElf_Sym *sym, @@ -150,17 +143,10 @@ static inline bool elf_sec__is_data(const GElf_Shdr *shdr, return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; } -static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs, - enum map_type type) +static bool elf_sec__filter(GElf_Shdr *shdr, Elf_Data *secstrs) { - switch (type) { - case MAP__FUNCTION: - return elf_sec__is_text(shdr, secstrs); - case MAP__VARIABLE: - return elf_sec__is_data(shdr, secstrs); - default: - return false; - } + return elf_sec__is_text(shdr, secstrs) || + elf_sec__is_data(shdr, secstrs); } static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) @@ -256,7 +242,7 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name) * And always look at the original dso, not at debuginfo packages, that * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). */ -int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map *map) +int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss) { uint32_t nr_rel_entries, idx; GElf_Sym sym; @@ -364,12 +350,12 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map * free(demangled); f = symbol__new(plt_offset, plt_entry_size, - STB_GLOBAL, sympltname); + STB_GLOBAL, STT_FUNC, sympltname); if (!f) goto out_elf_end; plt_offset += plt_entry_size; - symbols__insert(&dso->symbols[map->type], f); + symbols__insert(&dso->symbols, f); ++nr; } } else if (shdr_rel_plt.sh_type == SHT_REL) { @@ -390,12 +376,12 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map * free(demangled); f = symbol__new(plt_offset, plt_entry_size, - STB_GLOBAL, sympltname); + STB_GLOBAL, STT_FUNC, sympltname); if (!f) goto out_elf_end; plt_offset += plt_entry_size; - symbols__insert(&dso->symbols[map->type], f); + symbols__insert(&dso->symbols, f); ++nr; } } @@ -811,6 +797,110 @@ static u64 ref_reloc(struct kmap *kmap) void __weak arch__sym_update(struct symbol *s __maybe_unused, GElf_Sym *sym __maybe_unused) { } +static int dso__process_kernel_symbol(struct dso *dso, struct map *map, + GElf_Sym *sym, GElf_Shdr *shdr, + struct map_groups *kmaps, struct kmap *kmap, + struct dso **curr_dsop, struct map **curr_mapp, + const char *section_name, + bool adjust_kernel_syms, bool kmodule, bool *remap_kernel) +{ + struct dso *curr_dso = *curr_dsop; + struct map *curr_map; + char dso_name[PATH_MAX]; + + /* Adjust symbol to map to file offset */ + if (adjust_kernel_syms) + sym->st_value -= shdr->sh_addr - shdr->sh_offset; + + if (strcmp(section_name, (curr_dso->short_name + dso->short_name_len)) == 0) + return 0; + + if (strcmp(section_name, ".text") == 0) { + /* + * The initial kernel mapping is based on + * kallsyms and identity maps. Overwrite it to + * map to the kernel dso. + */ + if (*remap_kernel && dso->kernel) { + *remap_kernel = false; + map->start = shdr->sh_addr + ref_reloc(kmap); + map->end = map->start + shdr->sh_size; + map->pgoff = shdr->sh_offset; + map->map_ip = map__map_ip; + map->unmap_ip = map__unmap_ip; + /* Ensure maps are correctly ordered */ + if (kmaps) { + map__get(map); + map_groups__remove(kmaps, map); + map_groups__insert(kmaps, map); + map__put(map); + } + } + + /* + * The initial module mapping is based on + * /proc/modules mapped to offset zero. + * Overwrite it to map to the module dso. + */ + if (*remap_kernel && kmodule) { + *remap_kernel = false; + map->pgoff = shdr->sh_offset; + } + + *curr_mapp = map; + *curr_dsop = dso; + return 0; + } + + if (!kmap) + return 0; + + snprintf(dso_name, sizeof(dso_name), "%s%s", dso->short_name, section_name); + + curr_map = map_groups__find_by_name(kmaps, dso_name); + if (curr_map == NULL) { + u64 start = sym->st_value; + + if (kmodule) + start += map->start + shdr->sh_offset; + + curr_dso = dso__new(dso_name); + if (curr_dso == NULL) + return -1; + curr_dso->kernel = dso->kernel; + curr_dso->long_name = dso->long_name; + curr_dso->long_name_len = dso->long_name_len; + curr_map = map__new2(start, curr_dso); + dso__put(curr_dso); + if (curr_map == NULL) + return -1; + + if (adjust_kernel_syms) { + curr_map->start = shdr->sh_addr + ref_reloc(kmap); + curr_map->end = curr_map->start + shdr->sh_size; + curr_map->pgoff = shdr->sh_offset; + } else { + curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; + } + curr_dso->symtab_type = dso->symtab_type; + map_groups__insert(kmaps, curr_map); + /* + * Add it before we drop the referece to curr_map, i.e. while + * we still are sure to have a reference to this DSO via + * *curr_map->dso. + */ + dsos__add(&map->groups->machine->dsos, curr_dso); + /* kmaps already got it */ + map__put(curr_map); + dso__set_loaded(curr_dso); + *curr_mapp = curr_map; + *curr_dsop = curr_dso; + } else + *curr_dsop = curr_map->dso; + + return 0; +} + int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, struct symsrc *runtime_ss, int kmodule) { @@ -844,7 +934,7 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, * have the wrong values for the dso maps, so remove them. */ if (kmodule && syms_ss->symtab) - symbols__delete(&dso->symbols[map->type]); + symbols__delete(&dso->symbols); if (!syms_ss->symtab) { /* @@ -921,10 +1011,10 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, dso->adjust_symbols = runtime_ss->adjust_symbols || ref_reloc(kmap); /* - * Initial kernel and module mappings do not map to the dso. For - * function mappings, flag the fixups. + * Initial kernel and module mappings do not map to the dso. + * Flag the fixups. */ - if (map->type == MAP__FUNCTION && (dso->kernel || kmodule)) { + if (dso->kernel || kmodule) { remap_kernel = true; adjust_kernel_syms = dso->adjust_symbols; } @@ -936,7 +1026,7 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, const char *section_name; bool used_opd = false; - if (!is_label && !elf_sym__is_a(&sym, map->type)) + if (!is_label && !elf_sym__filter(&sym)) continue; /* Reject ARM ELF "mapping symbols": these aren't unique and @@ -974,7 +1064,7 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, gelf_getshdr(sec, &shdr); - if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) + if (is_label && !elf_sec__filter(&shdr, secstrs)) continue; section_name = elf_sec__name(&shdr, secstrs); @@ -982,134 +1072,37 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, /* On ARM, symbols for thumb functions have 1 added to * the symbol address as a flag - remove it */ if ((ehdr.e_machine == EM_ARM) && - (map->type == MAP__FUNCTION) && + (GELF_ST_TYPE(sym.st_info) == STT_FUNC) && (sym.st_value & 1)) --sym.st_value; if (dso->kernel || kmodule) { - char dso_name[PATH_MAX]; - - /* Adjust symbol to map to file offset */ - if (adjust_kernel_syms) - sym.st_value -= shdr.sh_addr - shdr.sh_offset; - - if (strcmp(section_name, - (curr_dso->short_name + - dso->short_name_len)) == 0) - goto new_symbol; - - if (strcmp(section_name, ".text") == 0) { - /* - * The initial kernel mapping is based on - * kallsyms and identity maps. Overwrite it to - * map to the kernel dso. - */ - if (remap_kernel && dso->kernel) { - remap_kernel = false; - map->start = shdr.sh_addr + - ref_reloc(kmap); - map->end = map->start + shdr.sh_size; - map->pgoff = shdr.sh_offset; - map->map_ip = map__map_ip; - map->unmap_ip = map__unmap_ip; - /* Ensure maps are correctly ordered */ - if (kmaps) { - map__get(map); - map_groups__remove(kmaps, map); - map_groups__insert(kmaps, map); - map__put(map); - } - } - - /* - * The initial module mapping is based on - * /proc/modules mapped to offset zero. - * Overwrite it to map to the module dso. - */ - if (remap_kernel && kmodule) { - remap_kernel = false; - map->pgoff = shdr.sh_offset; - } - - curr_map = map; - curr_dso = dso; - goto new_symbol; - } - - if (!kmap) - goto new_symbol; - - snprintf(dso_name, sizeof(dso_name), - "%s%s", dso->short_name, section_name); - - curr_map = map_groups__find_by_name(kmaps, map->type, dso_name); - if (curr_map == NULL) { - u64 start = sym.st_value; - - if (kmodule) - start += map->start + shdr.sh_offset; - - curr_dso = dso__new(dso_name); - if (curr_dso == NULL) - goto out_elf_end; - curr_dso->kernel = dso->kernel; - curr_dso->long_name = dso->long_name; - curr_dso->long_name_len = dso->long_name_len; - curr_map = map__new2(start, curr_dso, - map->type); - dso__put(curr_dso); - if (curr_map == NULL) { - goto out_elf_end; - } - if (adjust_kernel_syms) { - curr_map->start = shdr.sh_addr + - ref_reloc(kmap); - curr_map->end = curr_map->start + - shdr.sh_size; - curr_map->pgoff = shdr.sh_offset; - } else { - curr_map->map_ip = identity__map_ip; - curr_map->unmap_ip = identity__map_ip; - } - curr_dso->symtab_type = dso->symtab_type; - map_groups__insert(kmaps, curr_map); - /* - * Add it before we drop the referece to curr_map, - * i.e. while we still are sure to have a reference - * to this DSO via curr_map->dso. - */ - dsos__add(&map->groups->machine->dsos, curr_dso); - /* kmaps already got it */ - map__put(curr_map); - dso__set_loaded(curr_dso, map->type); - } else - curr_dso = curr_map->dso; - - goto new_symbol; - } - - if ((used_opd && runtime_ss->adjust_symbols) - || (!used_opd && syms_ss->adjust_symbols)) { + if (dso__process_kernel_symbol(dso, map, &sym, &shdr, kmaps, kmap, &curr_dso, &curr_map, + section_name, adjust_kernel_syms, kmodule, &remap_kernel)) + goto out_elf_end; + } else if ((used_opd && runtime_ss->adjust_symbols) || + (!used_opd && syms_ss->adjust_symbols)) { pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " " "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__, (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); sym.st_value -= shdr.sh_addr - shdr.sh_offset; } -new_symbol: + demangled = demangle_sym(dso, kmodule, elf_name); if (demangled != NULL) elf_name = demangled; f = symbol__new(sym.st_value, sym.st_size, - GELF_ST_BIND(sym.st_info), elf_name); + GELF_ST_BIND(sym.st_info), + GELF_ST_TYPE(sym.st_info), elf_name); free(demangled); if (!f) goto out_elf_end; arch__sym_update(f, &sym); - __symbols__insert(&curr_dso->symbols[curr_map->type], f, dso->kernel); + __symbols__insert(&curr_dso->symbols, f, dso->kernel); nr++; } @@ -1117,14 +1110,14 @@ new_symbol: * For misannotated, zeroed, ASM function sizes. */ if (nr > 0) { - symbols__fixup_end(&dso->symbols[map->type]); - symbols__fixup_duplicate(&dso->symbols[map->type]); + symbols__fixup_end(&dso->symbols); + symbols__fixup_duplicate(&dso->symbols); if (kmap) { /* * We need to fixup this here too because we create new * maps here, for things like vsyscall sections. */ - __map_groups__fixup_end(kmaps, map->type); + map_groups__fixup_end(kmaps); } } err = nr; @@ -1393,8 +1386,16 @@ static off_t kcore__write(struct kcore *kcore) struct phdr_data { off_t offset; + off_t rel; u64 addr; u64 len; + struct list_head node; + struct phdr_data *remaps; +}; + +struct sym_data { + u64 addr; + struct list_head node; }; struct kcore_copy_info { @@ -1404,16 +1405,78 @@ struct kcore_copy_info { u64 last_symbol; u64 first_module; u64 last_module_symbol; - struct phdr_data kernel_map; - struct phdr_data modules_map; + size_t phnum; + struct list_head phdrs; + struct list_head syms; }; +#define kcore_copy__for_each_phdr(k, p) \ + list_for_each_entry((p), &(k)->phdrs, node) + +static struct phdr_data *phdr_data__new(u64 addr, u64 len, off_t offset) +{ + struct phdr_data *p = zalloc(sizeof(*p)); + + if (p) { + p->addr = addr; + p->len = len; + p->offset = offset; + } + + return p; +} + +static struct phdr_data *kcore_copy_info__addnew(struct kcore_copy_info *kci, + u64 addr, u64 len, + off_t offset) +{ + struct phdr_data *p = phdr_data__new(addr, len, offset); + + if (p) + list_add_tail(&p->node, &kci->phdrs); + + return p; +} + +static void kcore_copy__free_phdrs(struct kcore_copy_info *kci) +{ + struct phdr_data *p, *tmp; + + list_for_each_entry_safe(p, tmp, &kci->phdrs, node) { + list_del(&p->node); + free(p); + } +} + +static struct sym_data *kcore_copy__new_sym(struct kcore_copy_info *kci, + u64 addr) +{ + struct sym_data *s = zalloc(sizeof(*s)); + + if (s) { + s->addr = addr; + list_add_tail(&s->node, &kci->syms); + } + + return s; +} + +static void kcore_copy__free_syms(struct kcore_copy_info *kci) +{ + struct sym_data *s, *tmp; + + list_for_each_entry_safe(s, tmp, &kci->syms, node) { + list_del(&s->node); + free(s); + } +} + static int kcore_copy__process_kallsyms(void *arg, const char *name, char type, u64 start) { struct kcore_copy_info *kci = arg; - if (!symbol_type__is_a(type, MAP__FUNCTION)) + if (!kallsyms__is_function(type)) return 0; if (strchr(name, '[')) { @@ -1438,6 +1501,9 @@ static int kcore_copy__process_kallsyms(void *arg, const char *name, char type, return 0; } + if (is_entry_trampoline(name) && !kcore_copy__new_sym(kci, start)) + return -1; + return 0; } @@ -1487,27 +1553,39 @@ static int kcore_copy__parse_modules(struct kcore_copy_info *kci, return 0; } -static void kcore_copy__map(struct phdr_data *p, u64 start, u64 end, u64 pgoff, - u64 s, u64 e) +static int kcore_copy__map(struct kcore_copy_info *kci, u64 start, u64 end, + u64 pgoff, u64 s, u64 e) { - if (p->addr || s < start || s >= end) - return; + u64 len, offset; + + if (s < start || s >= end) + return 0; - p->addr = s; - p->offset = (s - start) + pgoff; - p->len = e < end ? e - s : end - s; + offset = (s - start) + pgoff; + len = e < end ? e - s : end - s; + + return kcore_copy_info__addnew(kci, s, len, offset) ? 0 : -1; } static int kcore_copy__read_map(u64 start, u64 len, u64 pgoff, void *data) { struct kcore_copy_info *kci = data; u64 end = start + len; + struct sym_data *sdat; - kcore_copy__map(&kci->kernel_map, start, end, pgoff, kci->stext, - kci->etext); + if (kcore_copy__map(kci, start, end, pgoff, kci->stext, kci->etext)) + return -1; - kcore_copy__map(&kci->modules_map, start, end, pgoff, kci->first_module, - kci->last_module_symbol); + if (kcore_copy__map(kci, start, end, pgoff, kci->first_module, + kci->last_module_symbol)) + return -1; + + list_for_each_entry(sdat, &kci->syms, node) { + u64 s = round_down(sdat->addr, page_size); + + if (kcore_copy__map(kci, start, end, pgoff, s, s + len)) + return -1; + } return 0; } @@ -1520,6 +1598,64 @@ static int kcore_copy__read_maps(struct kcore_copy_info *kci, Elf *elf) return 0; } +static void kcore_copy__find_remaps(struct kcore_copy_info *kci) +{ + struct phdr_data *p, *k = NULL; + u64 kend; + + if (!kci->stext) + return; + + /* Find phdr that corresponds to the kernel map (contains stext) */ + kcore_copy__for_each_phdr(kci, p) { + u64 pend = p->addr + p->len - 1; + + if (p->addr <= kci->stext && pend >= kci->stext) { + k = p; + break; + } + } + + if (!k) + return; + + kend = k->offset + k->len; + + /* Find phdrs that remap the kernel */ + kcore_copy__for_each_phdr(kci, p) { + u64 pend = p->offset + p->len; + + if (p == k) + continue; + + if (p->offset >= k->offset && pend <= kend) + p->remaps = k; + } +} + +static void kcore_copy__layout(struct kcore_copy_info *kci) +{ + struct phdr_data *p; + off_t rel = 0; + + kcore_copy__find_remaps(kci); + + kcore_copy__for_each_phdr(kci, p) { + if (!p->remaps) { + p->rel = rel; + rel += p->len; + } + kci->phnum += 1; + } + + kcore_copy__for_each_phdr(kci, p) { + struct phdr_data *k = p->remaps; + + if (k) + p->rel = p->offset - k->offset + k->rel; + } +} + static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir, Elf *elf) { @@ -1555,7 +1691,12 @@ static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir, if (kci->first_module && !kci->last_module_symbol) return -1; - return kcore_copy__read_maps(kci, elf); + if (kcore_copy__read_maps(kci, elf)) + return -1; + + kcore_copy__layout(kci); + + return 0; } static int kcore_copy__copy_file(const char *from_dir, const char *to_dir, @@ -1678,12 +1819,15 @@ int kcore_copy(const char *from_dir, const char *to_dir) { struct kcore kcore; struct kcore extract; - size_t count = 2; int idx = 0, err = -1; - off_t offset = page_size, sz, modules_offset = 0; + off_t offset, sz; struct kcore_copy_info kci = { .stext = 0, }; char kcore_filename[PATH_MAX]; char extract_filename[PATH_MAX]; + struct phdr_data *p; + + INIT_LIST_HEAD(&kci.phdrs); + INIT_LIST_HEAD(&kci.syms); if (kcore_copy__copy_file(from_dir, to_dir, "kallsyms")) return -1; @@ -1703,20 +1847,17 @@ int kcore_copy(const char *from_dir, const char *to_dir) if (kcore__init(&extract, extract_filename, kcore.elfclass, false)) goto out_kcore_close; - if (!kci.modules_map.addr) - count -= 1; - - if (kcore__copy_hdr(&kcore, &extract, count)) + if (kcore__copy_hdr(&kcore, &extract, kci.phnum)) goto out_extract_close; - if (kcore__add_phdr(&extract, idx++, offset, kci.kernel_map.addr, - kci.kernel_map.len)) - goto out_extract_close; + offset = gelf_fsize(extract.elf, ELF_T_EHDR, 1, EV_CURRENT) + + gelf_fsize(extract.elf, ELF_T_PHDR, kci.phnum, EV_CURRENT); + offset = round_up(offset, page_size); + + kcore_copy__for_each_phdr(&kci, p) { + off_t offs = p->rel + offset; - if (kci.modules_map.addr) { - modules_offset = offset + kci.kernel_map.len; - if (kcore__add_phdr(&extract, idx, modules_offset, - kci.modules_map.addr, kci.modules_map.len)) + if (kcore__add_phdr(&extract, idx++, offs, p->addr, p->len)) goto out_extract_close; } @@ -1724,14 +1865,14 @@ int kcore_copy(const char *from_dir, const char *to_dir) if (sz < 0 || sz > offset) goto out_extract_close; - if (copy_bytes(kcore.fd, kci.kernel_map.offset, extract.fd, offset, - kci.kernel_map.len)) - goto out_extract_close; + kcore_copy__for_each_phdr(&kci, p) { + off_t offs = p->rel + offset; - if (modules_offset && copy_bytes(kcore.fd, kci.modules_map.offset, - extract.fd, modules_offset, - kci.modules_map.len)) - goto out_extract_close; + if (p->remaps) + continue; + if (copy_bytes(kcore.fd, p->offset, extract.fd, offs, p->len)) + goto out_extract_close; + } if (kcore_copy__compare_file(from_dir, to_dir, "modules")) goto out_extract_close; @@ -1754,6 +1895,9 @@ out_unlink_kallsyms: if (err) kcore_copy__unlink(to_dir, "kallsyms"); + kcore_copy__free_phdrs(&kci); + kcore_copy__free_syms(&kci); + return err; } diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index ff48d0d49584..7119df77dc0b 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -288,8 +288,7 @@ void symsrc__destroy(struct symsrc *ss) } int dso__synthesize_plt_symbols(struct dso *dso __maybe_unused, - struct symsrc *ss __maybe_unused, - struct map *map __maybe_unused) + struct symsrc *ss __maybe_unused) { return 0; } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 1466814ebada..8c84437f2a10 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -5,6 +5,7 @@ #include <stdio.h> #include <string.h> #include <linux/kernel.h> +#include <linux/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/param.h> @@ -70,18 +71,10 @@ static enum dso_binary_type binary_type_symtab[] = { #define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab) -bool symbol_type__is_a(char symbol_type, enum map_type map_type) +static bool symbol_type__filter(char symbol_type) { symbol_type = toupper(symbol_type); - - switch (map_type) { - case MAP__FUNCTION: - return symbol_type == 'T' || symbol_type == 'W'; - case MAP__VARIABLE: - return symbol_type == 'D'; - default: - return false; - } + return symbol_type == 'T' || symbol_type == 'W' || symbol_type == 'D'; } static int prefix_underscores_count(const char *str) @@ -228,9 +221,9 @@ void symbols__fixup_end(struct rb_root *symbols) curr->end = roundup(curr->start, 4096) + 4096; } -void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) +void map_groups__fixup_end(struct map_groups *mg) { - struct maps *maps = &mg->maps[type]; + struct maps *maps = &mg->maps; struct map *next, *curr; down_write(&maps->lock); @@ -256,7 +249,7 @@ out_unlock: up_write(&maps->lock); } -struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) +struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name) { size_t namelen = strlen(name) + 1; struct symbol *sym = calloc(1, (symbol_conf.priv_size + @@ -274,6 +267,7 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) sym->start = start; sym->end = len ? start + len : start; + sym->type = type; sym->binding = binding; sym->namelen = namelen - 1; @@ -484,45 +478,40 @@ static struct symbol *symbols__find_by_name(struct rb_root *symbols, void dso__reset_find_symbol_cache(struct dso *dso) { - enum map_type type; - - for (type = MAP__FUNCTION; type <= MAP__VARIABLE; ++type) { - dso->last_find_result[type].addr = 0; - dso->last_find_result[type].symbol = NULL; - } + dso->last_find_result.addr = 0; + dso->last_find_result.symbol = NULL; } -void dso__insert_symbol(struct dso *dso, enum map_type type, struct symbol *sym) +void dso__insert_symbol(struct dso *dso, struct symbol *sym) { - __symbols__insert(&dso->symbols[type], sym, dso->kernel); + __symbols__insert(&dso->symbols, sym, dso->kernel); /* update the symbol cache if necessary */ - if (dso->last_find_result[type].addr >= sym->start && - (dso->last_find_result[type].addr < sym->end || + if (dso->last_find_result.addr >= sym->start && + (dso->last_find_result.addr < sym->end || sym->start == sym->end)) { - dso->last_find_result[type].symbol = sym; + dso->last_find_result.symbol = sym; } } -struct symbol *dso__find_symbol(struct dso *dso, - enum map_type type, u64 addr) +struct symbol *dso__find_symbol(struct dso *dso, u64 addr) { - if (dso->last_find_result[type].addr != addr || dso->last_find_result[type].symbol == NULL) { - dso->last_find_result[type].addr = addr; - dso->last_find_result[type].symbol = symbols__find(&dso->symbols[type], addr); + if (dso->last_find_result.addr != addr || dso->last_find_result.symbol == NULL) { + dso->last_find_result.addr = addr; + dso->last_find_result.symbol = symbols__find(&dso->symbols, addr); } - return dso->last_find_result[type].symbol; + return dso->last_find_result.symbol; } -struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) +struct symbol *dso__first_symbol(struct dso *dso) { - return symbols__first(&dso->symbols[type]); + return symbols__first(&dso->symbols); } -struct symbol *dso__last_symbol(struct dso *dso, enum map_type type) +struct symbol *dso__last_symbol(struct dso *dso) { - return symbols__last(&dso->symbols[type]); + return symbols__last(&dso->symbols); } struct symbol *dso__next_symbol(struct symbol *sym) @@ -539,24 +528,22 @@ struct symbol *symbol__next_by_name(struct symbol *sym) } /* - * Teturns first symbol that matched with @name. + * Returns first symbol that matched with @name. */ -struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, - const char *name) +struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name) { - struct symbol *s = symbols__find_by_name(&dso->symbol_names[type], name, + struct symbol *s = symbols__find_by_name(&dso->symbol_names, name, SYMBOL_TAG_INCLUDE__NONE); if (!s) - s = symbols__find_by_name(&dso->symbol_names[type], name, + s = symbols__find_by_name(&dso->symbol_names, name, SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); return s; } -void dso__sort_by_name(struct dso *dso, enum map_type type) +void dso__sort_by_name(struct dso *dso) { - dso__set_sorted_by_name(dso, type); - return symbols__sort_by_name(&dso->symbol_names[type], - &dso->symbols[type]); + dso__set_sorted_by_name(dso); + return symbols__sort_by_name(&dso->symbol_names, &dso->symbols); } int modules__parse(const char *filename, void *arg, @@ -621,11 +608,6 @@ out: return err; } -struct process_kallsyms_args { - struct map *map; - struct dso *dso; -}; - /* * These are symbols in the kernel image, so make sure that * sym is from a kernel DSO. @@ -661,10 +643,10 @@ static int map__process_kallsym_symbol(void *arg, const char *name, char type, u64 start) { struct symbol *sym; - struct process_kallsyms_args *a = arg; - struct rb_root *root = &a->dso->symbols[a->map->type]; + struct dso *dso = arg; + struct rb_root *root = &dso->symbols; - if (!symbol_type__is_a(type, a->map->type)) + if (!symbol_type__filter(type)) return 0; /* @@ -672,7 +654,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, * symbols, setting length to 0, and rely on * symbols__fixup_end() to fix it up. */ - sym = symbol__new(start, 0, kallsyms2elf_binding(type), name); + sym = symbol__new(start, 0, kallsyms2elf_binding(type), kallsyms2elf_type(type), name); if (sym == NULL) return -ENOMEM; /* @@ -689,21 +671,18 @@ static int map__process_kallsym_symbol(void *arg, const char *name, * so that we can in the next step set the symbol ->end address and then * call kernel_maps__split_kallsyms. */ -static int dso__load_all_kallsyms(struct dso *dso, const char *filename, - struct map *map) +static int dso__load_all_kallsyms(struct dso *dso, const char *filename) { - struct process_kallsyms_args args = { .map = map, .dso = dso, }; - return kallsyms__parse(filename, &args, map__process_kallsym_symbol); + return kallsyms__parse(filename, dso, map__process_kallsym_symbol); } -static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map) +static int map_groups__split_kallsyms_for_kcore(struct map_groups *kmaps, struct dso *dso) { - struct map_groups *kmaps = map__kmaps(map); struct map *curr_map; struct symbol *pos; int count = 0; - struct rb_root old_root = dso->symbols[map->type]; - struct rb_root *root = &dso->symbols[map->type]; + struct rb_root old_root = dso->symbols; + struct rb_root *root = &dso->symbols; struct rb_node *next = rb_first(root); if (!kmaps) @@ -723,7 +702,7 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map) if (module) *module = '\0'; - curr_map = map_groups__find(kmaps, map->type, pos->start); + curr_map = map_groups__find(kmaps, pos->start); if (!curr_map) { symbol__delete(pos); @@ -733,7 +712,7 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map) pos->start -= curr_map->start - curr_map->pgoff; if (pos->end) pos->end -= curr_map->start - curr_map->pgoff; - symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); + symbols__insert(&curr_map->dso->symbols, pos); ++count; } @@ -748,22 +727,25 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map) * kernel range is broken in several maps, named [kernel].N, as we don't have * the original ELF section names vmlinux have. */ -static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta) +static int map_groups__split_kallsyms(struct map_groups *kmaps, struct dso *dso, u64 delta, + struct map *initial_map) { - struct map_groups *kmaps = map__kmaps(map); struct machine *machine; - struct map *curr_map = map; + struct map *curr_map = initial_map; struct symbol *pos; int count = 0, moved = 0; - struct rb_root *root = &dso->symbols[map->type]; + struct rb_root *root = &dso->symbols; struct rb_node *next = rb_first(root); int kernel_range = 0; + bool x86_64; if (!kmaps) return -1; machine = kmaps->machine; + x86_64 = machine__is(machine, "x86_64"); + while (next) { char *module; @@ -778,7 +760,7 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta) *module++ = '\0'; if (strcmp(curr_map->dso->short_name, module)) { - if (curr_map != map && + if (curr_map != initial_map && dso->kernel == DSO_TYPE_GUEST_KERNEL && machine__is_default_guest(machine)) { /* @@ -788,18 +770,16 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta) * symbols are in its kmap. Mark it as * loaded. */ - dso__set_loaded(curr_map->dso, - curr_map->type); + dso__set_loaded(curr_map->dso); } - curr_map = map_groups__find_by_name(kmaps, - map->type, module); + curr_map = map_groups__find_by_name(kmaps, module); if (curr_map == NULL) { pr_debug("%s/proc/{kallsyms,modules} " "inconsistency while looking " "for \"%s\" module!\n", machine->root_dir, module); - curr_map = map; + curr_map = initial_map; goto discard_symbol; } @@ -809,11 +789,21 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta) } /* * So that we look just like we get from .ko files, - * i.e. not prelinked, relative to map->start. + * i.e. not prelinked, relative to initial_map->start. */ pos->start = curr_map->map_ip(curr_map, pos->start); pos->end = curr_map->map_ip(curr_map, pos->end); - } else if (curr_map != map) { + } else if (x86_64 && is_entry_trampoline(pos->name)) { + /* + * These symbols are not needed anymore since the + * trampoline maps refer to the text section and it's + * symbols instead. Avoid having to deal with + * relocations, and the assumption that the first symbol + * is the start of kernel text, by simply removing the + * symbols at this point. + */ + goto discard_symbol; + } else if (curr_map != initial_map) { char dso_name[PATH_MAX]; struct dso *ndso; @@ -824,7 +814,7 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta) } if (count == 0) { - curr_map = map; + curr_map = initial_map; goto add_symbol; } @@ -843,7 +833,7 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta) ndso->kernel = dso->kernel; - curr_map = map__new2(pos->start, ndso, map->type); + curr_map = map__new2(pos->start, ndso); if (curr_map == NULL) { dso__put(ndso); return -1; @@ -858,9 +848,9 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta) pos->end -= delta; } add_symbol: - if (curr_map != map) { + if (curr_map != initial_map) { rb_erase(&pos->rb_node, root); - symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); + symbols__insert(&curr_map->dso->symbols, pos); ++moved; } else ++count; @@ -871,10 +861,10 @@ discard_symbol: symbol__delete(pos); } - if (curr_map != map && + if (curr_map != initial_map && dso->kernel == DSO_TYPE_GUEST_KERNEL && machine__is_default_guest(kmaps->machine)) { - dso__set_loaded(curr_map->dso, curr_map->type); + dso__set_loaded(curr_map->dso); } return count + moved; @@ -1035,7 +1025,12 @@ out_delete_from: return ret; } -static int do_validate_kcore_modules(const char *filename, struct map *map, +struct map *map_groups__first(struct map_groups *mg) +{ + return maps__first(&mg->maps); +} + +static int do_validate_kcore_modules(const char *filename, struct map_groups *kmaps) { struct rb_root modules = RB_ROOT; @@ -1046,13 +1041,12 @@ static int do_validate_kcore_modules(const char *filename, struct map *map, if (err) return err; - old_map = map_groups__first(kmaps, map->type); + old_map = map_groups__first(kmaps); while (old_map) { struct map *next = map_groups__next(old_map); struct module_info *mi; - if (old_map == map || old_map->start == map->start) { - /* The kernel map */ + if (!__map__is_kmodule(old_map)) { old_map = next; continue; } @@ -1109,7 +1103,7 @@ static int validate_kcore_modules(const char *kallsyms_filename, kallsyms_filename)) return -EINVAL; - if (do_validate_kcore_modules(modules_filename, map, kmaps)) + if (do_validate_kcore_modules(modules_filename, kmaps)) return -EINVAL; return 0; @@ -1138,7 +1132,6 @@ static int validate_kcore_addresses(const char *kallsyms_filename, struct kcore_mapfn_data { struct dso *dso; - enum map_type type; struct list_head maps; }; @@ -1147,7 +1140,7 @@ static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) struct kcore_mapfn_data *md = data; struct map *map; - map = map__new2(start, md->dso, md->type); + map = map__new2(start, md->dso); if (map == NULL) return -ENOMEM; @@ -1163,13 +1156,13 @@ static int dso__load_kcore(struct dso *dso, struct map *map, const char *kallsyms_filename) { struct map_groups *kmaps = map__kmaps(map); - struct machine *machine; struct kcore_mapfn_data md; struct map *old_map, *new_map, *replacement_map = NULL; + struct machine *machine; bool is_64_bit; int err, fd; char kcore_filename[PATH_MAX]; - struct symbol *sym; + u64 stext; if (!kmaps) return -EINVAL; @@ -1177,7 +1170,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map, machine = kmaps->machine; /* This function requires that the map is the kernel map */ - if (map != machine->vmlinux_maps[map->type]) + if (!__map__is_kernel(map)) return -EINVAL; if (!filename_from_kallsyms_filename(kcore_filename, "kcore", @@ -1189,7 +1182,6 @@ static int dso__load_kcore(struct dso *dso, struct map *map, return -EINVAL; md.dso = dso; - md.type = map->type; INIT_LIST_HEAD(&md.maps); fd = open(kcore_filename, O_RDONLY); @@ -1200,7 +1192,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map, } /* Read new maps into temporary lists */ - err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md, + err = file__read_maps(fd, map->prot & PROT_EXEC, kcore_mapfn, &md, &is_64_bit); if (err) goto out_err; @@ -1212,7 +1204,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map, } /* Remove old maps */ - old_map = map_groups__first(kmaps, map->type); + old_map = map_groups__first(kmaps); while (old_map) { struct map *next = map_groups__next(old_map); @@ -1220,14 +1212,15 @@ static int dso__load_kcore(struct dso *dso, struct map *map, map_groups__remove(kmaps, old_map); old_map = next; } + machine->trampolines_mapped = false; - /* Find the kernel map using the first symbol */ - sym = dso__first_symbol(dso, map->type); - list_for_each_entry(new_map, &md.maps, node) { - if (sym && sym->start >= new_map->start && - sym->start < new_map->end) { - replacement_map = new_map; - break; + /* Find the kernel map using the '_stext' symbol */ + if (!kallsyms__get_function_start(kallsyms_filename, "_stext", &stext)) { + list_for_each_entry(new_map, &md.maps, node) { + if (stext >= new_map->start && stext < new_map->end) { + replacement_map = new_map; + break; + } } } @@ -1256,6 +1249,19 @@ static int dso__load_kcore(struct dso *dso, struct map *map, map__put(new_map); } + if (machine__is(machine, "x86_64")) { + u64 addr; + + /* + * If one of the corresponding symbols is there, assume the + * entry trampoline maps are too. + */ + if (!kallsyms__get_function_start(kallsyms_filename, + ENTRY_TRAMPOLINE_NAME, + &addr)) + machine->trampolines_mapped = true; + } + /* * Set the data type and long name so that kcore can be read via * dso__data_read_addr(). @@ -1268,7 +1274,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map, close(fd); - if (map->type == MAP__FUNCTION) + if (map->prot & PROT_EXEC) pr_debug("Using %s for kernel object code\n", kcore_filename); else pr_debug("Using %s for kernel data\n", kcore_filename); @@ -1289,14 +1295,10 @@ out_err: * If the kernel is relocated at boot time, kallsyms won't match. Compute the * delta based on the relocation reference symbol. */ -static int kallsyms__delta(struct map *map, const char *filename, u64 *delta) +static int kallsyms__delta(struct kmap *kmap, const char *filename, u64 *delta) { - struct kmap *kmap = map__kmap(map); u64 addr; - if (!kmap) - return -1; - if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->name) return 0; @@ -1310,19 +1312,23 @@ static int kallsyms__delta(struct map *map, const char *filename, u64 *delta) int __dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, bool no_kcore) { + struct kmap *kmap = map__kmap(map); u64 delta = 0; if (symbol__restricted_filename(filename, "/proc/kallsyms")) return -1; - if (dso__load_all_kallsyms(dso, filename, map) < 0) + if (!kmap || !kmap->kmaps) return -1; - if (kallsyms__delta(map, filename, &delta)) + if (dso__load_all_kallsyms(dso, filename) < 0) return -1; - symbols__fixup_end(&dso->symbols[map->type]); - symbols__fixup_duplicate(&dso->symbols[map->type]); + if (kallsyms__delta(kmap, filename, &delta)) + return -1; + + symbols__fixup_end(&dso->symbols); + symbols__fixup_duplicate(&dso->symbols); if (dso->kernel == DSO_TYPE_GUEST_KERNEL) dso->symtab_type = DSO_BINARY_TYPE__GUEST_KALLSYMS; @@ -1330,9 +1336,9 @@ int __dso__load_kallsyms(struct dso *dso, const char *filename, dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; if (!no_kcore && !dso__load_kcore(dso, map, filename)) - return dso__split_kallsyms_for_kcore(dso, map); + return map_groups__split_kallsyms_for_kcore(kmap->kmaps, dso); else - return dso__split_kallsyms(dso, map, delta); + return map_groups__split_kallsyms(kmap->kmaps, dso, delta, map); } int dso__load_kallsyms(struct dso *dso, const char *filename, @@ -1341,8 +1347,7 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, return __dso__load_kallsyms(dso, filename, map, false); } -static int dso__load_perf_map(const char *map_path, struct dso *dso, - struct map *map) +static int dso__load_perf_map(const char *map_path, struct dso *dso) { char *line = NULL; size_t n; @@ -1379,12 +1384,12 @@ static int dso__load_perf_map(const char *map_path, struct dso *dso, if (len + 2 >= line_len) continue; - sym = symbol__new(start, size, STB_GLOBAL, line + len); + sym = symbol__new(start, size, STB_GLOBAL, STT_FUNC, line + len); if (sym == NULL) goto out_delete_line; - symbols__insert(&dso->symbols[map->type], sym); + symbols__insert(&dso->symbols, sym); nr_syms++; } @@ -1509,25 +1514,27 @@ int dso__load(struct dso *dso, struct map *map) pthread_mutex_lock(&dso->lock); /* check again under the dso->lock */ - if (dso__loaded(dso, map->type)) { + if (dso__loaded(dso)) { ret = 1; goto out; } + if (map->groups && map->groups->machine) + machine = map->groups->machine; + else + machine = NULL; + if (dso->kernel) { if (dso->kernel == DSO_TYPE_KERNEL) ret = dso__load_kernel_sym(dso, map); else if (dso->kernel == DSO_TYPE_GUEST_KERNEL) ret = dso__load_guest_kernel_sym(dso, map); + if (machine__is(machine, "x86_64")) + machine__map_x86_64_entry_trampolines(machine, dso); goto out; } - if (map->groups && map->groups->machine) - machine = map->groups->machine; - else - machine = NULL; - dso->adjust_symbols = 0; if (perfmap) { @@ -1542,7 +1549,7 @@ int dso__load(struct dso *dso, struct map *map) goto out; } - ret = dso__load_perf_map(map_path, dso, map); + ret = dso__load_perf_map(map_path, dso); dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT : DSO_BINARY_TYPE__NOT_FOUND; goto out; @@ -1651,7 +1658,7 @@ int dso__load(struct dso *dso, struct map *map) if (ret > 0) { int nr_plt; - nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss, map); + nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss); if (nr_plt > 0) ret += nr_plt; } @@ -1663,17 +1670,16 @@ out_free: if (ret < 0 && strstr(dso->name, " (deleted)") != NULL) ret = 0; out: - dso__set_loaded(dso, map->type); + dso__set_loaded(dso); pthread_mutex_unlock(&dso->lock); nsinfo__mountns_exit(&nsc); return ret; } -struct map *map_groups__find_by_name(struct map_groups *mg, - enum map_type type, const char *name) +struct map *map_groups__find_by_name(struct map_groups *mg, const char *name) { - struct maps *maps = &mg->maps[type]; + struct maps *maps = &mg->maps; struct map *map; down_read(&maps->lock); @@ -1720,7 +1726,7 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, else dso->binary_type = DSO_BINARY_TYPE__VMLINUX; dso__set_long_name(dso, vmlinux, vmlinux_allocated); - dso__set_loaded(dso, map->type); + dso__set_loaded(dso); pr_debug("Using %s for symbols\n", symfs_vmlinux); } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 70c16741f50a..1a16438eb3ce 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -57,7 +57,8 @@ struct symbol { u64 start; u64 end; u16 namelen; - u8 binding; + u8 type:4; + u8 binding:4; u8 idle:1; u8 ignore:1; u8 inlined:1; @@ -259,17 +260,16 @@ int __dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, bool no_kcore); int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map); -void dso__insert_symbol(struct dso *dso, enum map_type type, +void dso__insert_symbol(struct dso *dso, struct symbol *sym); -struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, - u64 addr); -struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, - const char *name); +struct symbol *dso__find_symbol(struct dso *dso, u64 addr); +struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name); + struct symbol *symbol__next_by_name(struct symbol *sym); -struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); -struct symbol *dso__last_symbol(struct dso *dso, enum map_type type); +struct symbol *dso__first_symbol(struct dso *dso); +struct symbol *dso__last_symbol(struct dso *dso); struct symbol *dso__next_symbol(struct symbol *sym); enum dso_type dso__type_fd(int fd); @@ -288,7 +288,7 @@ void symbol__exit(void); void symbol__elf_init(void); int symbol__annotation_init(void); -struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); +struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name); size_t __symbol__fprintf_symname_offs(const struct symbol *sym, const struct addr_location *al, bool unknown_as_addr, @@ -300,7 +300,6 @@ size_t __symbol__fprintf_symname(const struct symbol *sym, bool unknown_as_addr, FILE *fp); size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); size_t symbol__fprintf(struct symbol *sym, FILE *fp); -bool symbol_type__is_a(char symbol_type, enum map_type map_type); bool symbol__restricted_filename(const char *filename, const char *restricted_filename); int symbol__config_symfs(const struct option *opt __maybe_unused, @@ -308,8 +307,7 @@ int symbol__config_symfs(const struct option *opt __maybe_unused, int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, struct symsrc *runtime_ss, int kmodule); -int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, - struct map *map); +int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss); char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name); @@ -317,7 +315,7 @@ void __symbols__insert(struct rb_root *symbols, struct symbol *sym, bool kernel) void symbols__insert(struct rb_root *symbols, struct symbol *sym); void symbols__fixup_duplicate(struct rb_root *symbols); void symbols__fixup_end(struct rb_root *symbols); -void __map_groups__fixup_end(struct map_groups *mg, enum map_type type); +void map_groups__fixup_end(struct map_groups *mg); typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data); int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c index 6dd2cb88ccbe..ed0205cc7942 100644 --- a/tools/perf/util/symbol_fprintf.c +++ b/tools/perf/util/symbol_fprintf.c @@ -58,13 +58,13 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) } size_t dso__fprintf_symbols_by_name(struct dso *dso, - enum map_type type, FILE *fp) + FILE *fp) { size_t ret = 0; struct rb_node *nd; struct symbol_name_rb_node *pos; - for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&dso->symbol_names); nd; nd = rb_next(nd)) { pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); fprintf(fp, "%s\n", pos->sym.name); } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 68b65b10579b..2048d393ece6 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -302,23 +302,20 @@ int thread__insert_map(struct thread *thread, struct map *map) static int __thread__prepare_access(struct thread *thread) { bool initialized = false; - int i, err = 0; - - for (i = 0; i < MAP__NR_TYPES; ++i) { - struct maps *maps = &thread->mg->maps[i]; - struct map *map; + int err = 0; + struct maps *maps = &thread->mg->maps; + struct map *map; - down_read(&maps->lock); + down_read(&maps->lock); - for (map = maps__first(maps); map; map = map__next(map)) { - err = unwind__prepare_access(thread, map, &initialized); - if (err || initialized) - break; - } - - up_read(&maps->lock); + for (map = maps__first(maps); map; map = map__next(map)) { + err = unwind__prepare_access(thread, map, &initialized); + if (err || initialized) + break; } + up_read(&maps->lock); + return err; } @@ -335,8 +332,6 @@ static int thread__prepare_access(struct thread *thread) static int thread__clone_map_groups(struct thread *thread, struct thread *parent) { - int i; - /* This is new thread, we share map groups for process. */ if (thread->pid_ == parent->pid_) return thread__prepare_access(thread); @@ -348,9 +343,8 @@ static int thread__clone_map_groups(struct thread *thread, } /* But this one is new process, copy maps. */ - for (i = 0; i < MAP__NR_TYPES; ++i) - if (map_groups__clone(thread, parent->mg, i) < 0) - return -ENOMEM; + if (map_groups__clone(thread, parent->mg) < 0) + return -ENOMEM; return 0; } @@ -371,8 +365,7 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) return thread__clone_map_groups(thread, parent); } -void thread__find_cpumode_addr_location(struct thread *thread, - enum map_type type, u64 addr, +void thread__find_cpumode_addr_location(struct thread *thread, u64 addr, struct addr_location *al) { size_t i; @@ -384,7 +377,7 @@ void thread__find_cpumode_addr_location(struct thread *thread, }; for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { - thread__find_addr_location(thread, cpumodes[i], type, addr, al); + thread__find_symbol(thread, cpumodes[i], addr, al); if (al->map) break; } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 14d44c3235b8..07606aa6998d 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -92,16 +92,13 @@ size_t thread__fprintf(struct thread *thread, FILE *fp); struct thread *thread__main_thread(struct machine *machine, struct thread *thread); -void thread__find_addr_map(struct thread *thread, - u8 cpumode, enum map_type type, u64 addr, - struct addr_location *al); +struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, + struct addr_location *al); -void thread__find_addr_location(struct thread *thread, - u8 cpumode, enum map_type type, u64 addr, - struct addr_location *al); +struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode, + u64 addr, struct addr_location *al); -void thread__find_cpumode_addr_location(struct thread *thread, - enum map_type type, u64 addr, +void thread__find_cpumode_addr_location(struct thread *thread, u64 addr, struct addr_location *al); static inline void *thread__priv(struct thread *thread) diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index d7f2113462fb..c85d0d1a65ed 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -103,11 +103,10 @@ out: static int record_header_files(void) { - char *path; + char *path = get_events_file("header_page"); struct stat st; int err = -EIO; - path = get_tracing_file("events/header_page"); if (!path) { pr_debug("can't get tracing/events/header_page"); return -ENOMEM; @@ -128,9 +127,9 @@ static int record_header_files(void) goto out; } - put_tracing_file(path); + put_events_file(path); - path = get_tracing_file("events/header_event"); + path = get_events_file("header_event"); if (!path) { pr_debug("can't get tracing/events/header_event"); err = -ENOMEM; @@ -154,7 +153,7 @@ static int record_header_files(void) err = 0; out: - put_tracing_file(path); + put_events_file(path); return err; } @@ -243,7 +242,7 @@ static int record_ftrace_files(struct tracepoint_path *tps) char *path; int ret; - path = get_tracing_file("events/ftrace"); + path = get_events_file("ftrace"); if (!path) { pr_debug("can't get tracing/events/ftrace"); return -ENOMEM; diff --git a/tools/perf/util/trace-event.c b/tools/perf/util/trace-event.c index 16a776371d03..1aa368603268 100644 --- a/tools/perf/util/trace-event.c +++ b/tools/perf/util/trace-event.c @@ -75,6 +75,7 @@ void trace_event__cleanup(struct trace_event *t) static struct event_format* tp_format(const char *sys, const char *name) { + char *tp_dir = get_events_file(sys); struct pevent *pevent = tevent.pevent; struct event_format *event = NULL; char path[PATH_MAX]; @@ -82,8 +83,11 @@ tp_format(const char *sys, const char *name) char *data; int err; - scnprintf(path, PATH_MAX, "%s/%s/%s/format", - tracing_events_path, sys, name); + if (!tp_dir) + return ERR_PTR(-errno); + + scnprintf(path, PATH_MAX, "%s/%s/format", tp_dir, name); + put_events_file(tp_dir); err = filename__read_str(path, &data, &size); if (err) diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 7bdd239c795c..538db4e5d1e6 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -28,10 +28,11 @@ static int __report_module(struct addr_location *al, u64 ip, { Dwfl_Module *mod; struct dso *dso = NULL; - - thread__find_addr_location(ui->thread, - PERF_RECORD_MISC_USER, - MAP__FUNCTION, ip, al); + /* + * Some callers will use al->sym, so we can't just use the + * cheaper thread__find_map() here. + */ + thread__find_symbol(ui->thread, PERF_RECORD_MISC_USER, ip, al); if (al->map) dso = al->map->dso; @@ -103,19 +104,7 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, struct addr_location al; ssize_t size; - 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) { + if (!thread__find_map(ui->thread, PERF_RECORD_MISC_USER, addr, &al)) { pr_debug("unwind: no map for %lx\n", (unsigned long)addr); return -1; } diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index af873044d33a..6a11bc7e6b27 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -366,19 +366,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, static struct map *find_map(unw_word_t ip, struct unwind_info *ui) { struct addr_location al; - - 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; + return thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); } static int @@ -586,12 +574,9 @@ static int entry(u64 ip, struct thread *thread, struct unwind_entry e; struct addr_location al; - thread__find_addr_location(thread, PERF_RECORD_MISC_USER, - MAP__FUNCTION, ip, &al); - + e.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); e.ip = al.addr; e.map = al.map; - e.sym = al.sym; pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", al.sym ? al.sym->name : "''", diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 1019bbc5dbd8..eac5b858a371 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -38,11 +38,43 @@ void perf_set_multithreaded(void) } unsigned int page_size; -int cacheline_size; + +#ifdef _SC_LEVEL1_DCACHE_LINESIZE +#define cache_line_size(cacheline_sizep) *cacheline_sizep = sysconf(_SC_LEVEL1_DCACHE_LINESIZE) +#else +static void cache_line_size(int *cacheline_sizep) +{ + if (sysfs__read_int("devices/system/cpu/cpu0/cache/index0/coherency_line_size", cacheline_sizep)) + pr_debug("cannot determine cache line size"); +} +#endif + +int cacheline_size(void) +{ + static int size; + + if (!size) + cache_line_size(&size); + + return size; +} int sysctl_perf_event_max_stack = PERF_MAX_STACK_DEPTH; int sysctl_perf_event_max_contexts_per_stack = PERF_MAX_CONTEXTS_PER_STACK; +int sysctl__max_stack(void) +{ + int value; + + if (sysctl__read_int("kernel/perf_event_max_stack", &value) == 0) + sysctl_perf_event_max_stack = value; + + if (sysctl__read_int("kernel/perf_event_max_contexts_per_stack", &value) == 0) + sysctl_perf_event_max_contexts_per_stack = value; + + return sysctl_perf_event_max_stack; +} + bool test_attr__enabled; bool perf_host = true; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c9626c206208..dc58254a2b69 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -43,7 +43,9 @@ size_t hex_width(u64 v); int hex2u64(const char *ptr, u64 *val); extern unsigned int page_size; -extern int cacheline_size; +int __pure cacheline_size(void); + +int sysctl__max_stack(void); int fetch_kernel_version(unsigned int *puint, char *str, size_t str_sz); diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index 0acb1ec0e2f0..741af209b19d 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c @@ -139,12 +139,10 @@ static enum dso_type machine__thread_dso_type(struct machine *machine, struct thread *thread) { enum dso_type dso_type = DSO__TYPE_UNKNOWN; - struct map *map; - struct dso *dso; + struct map *map = map_groups__first(thread->mg); - map = map_groups__first(thread->mg, MAP__FUNCTION); for (; map ; map = map_groups__next(map)) { - dso = map->dso; + struct dso *dso = map->dso; if (!dso || dso->long_name[0] != '/') continue; dso_type = dso__type(dso, machine); |