diff options
Diffstat (limited to 'tools/perf/util/maps.c')
| -rw-r--r-- | tools/perf/util/maps.c | 206 |
1 files changed, 149 insertions, 57 deletions
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 4092211cff62..f808df2fe77b 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -40,6 +40,7 @@ DECLARE_RC_STRUCT(maps) { #ifdef HAVE_LIBUNWIND_SUPPORT void *addr_space; const struct unwind_libunwind_ops *unwind_libunwind_ops; + uint16_t e_machine; #endif #ifdef HAVE_LIBDW_SUPPORT void *libdw_addr_space_dwfl; @@ -197,14 +198,14 @@ void maps__set_addr_space(struct maps *maps, void *addr_space) RC_CHK_ACCESS(maps)->addr_space = addr_space; } -const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps) +uint16_t maps__e_machine(const struct maps *maps) { - return RC_CHK_ACCESS(maps)->unwind_libunwind_ops; + return RC_CHK_ACCESS(maps)->e_machine; } -void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops) +void maps__set_e_machine(struct maps *maps, uint16_t e_machine) { - RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops; + RC_CHK_ACCESS(maps)->e_machine = e_machine; } #endif #ifdef HAVE_LIBDW_SUPPORT @@ -575,6 +576,48 @@ void maps__remove(struct maps *maps, struct map *map) #endif } +/** + * maps__mutate_mapping - Apply write-protected mutations to a map. + * @maps: The maps collection containing the map. + * @map: The map to mutate. + * @mutate_cb: Callback function that performs the actual mutations. + * @data: Private data passed to the callback. + * + * This acquires the write lock on the maps semaphore to safely protect + * concurrent readers from seeing partially mutated or unsorted map boundaries. + * + * WARNING: Acquiring down_write() here can trigger a recursive self-deadlock if + * the caller already holds the read lock (e.g., during maps__for_each_map() or + * maps__find() iteration paths that trigger lazy symbol loading). To completely + * avoid this deadlock, all kernel/module maps must be pre-loaded up-front (via + * maps__load_maps()) under a clean, single-threaded context before entering + * multi-threaded event processing loops. + */ +int maps__mutate_mapping(struct maps *maps, struct map *map, + int (*mutate_cb)(struct map *map, void *data), void *data) +{ + int err = 0; + + if (maps) { + down_write(maps__lock(maps)); + + err = mutate_cb(map, data); + + RC_CHK_ACCESS(maps)->maps_by_address_sorted = false; + RC_CHK_ACCESS(maps)->maps_by_name_sorted = false; + + up_write(maps__lock(maps)); + +#ifdef HAVE_LIBDW_SUPPORT + libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps)); +#endif + } else { + err = mutate_cb(map, data); + } + + return err; +} + bool maps__empty(struct maps *maps) { bool res; @@ -625,6 +668,41 @@ int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data) return ret; } +int maps__load_maps(struct maps *maps) +{ + struct map **maps_copy; + unsigned int nr_maps; + int err = 0; + + if (!maps) + return 0; + + down_read(maps__lock(maps)); + nr_maps = maps__nr_maps(maps); + if (nr_maps == 0) { + up_read(maps__lock(maps)); + return 0; + } + maps_copy = calloc(nr_maps, sizeof(*maps_copy)); + if (!maps_copy) { + up_read(maps__lock(maps)); + return -ENOMEM; + } + for (unsigned int i = 0; i < nr_maps; i++) + maps_copy[i] = map__get(maps__maps_by_address(maps)[i]); + up_read(maps__lock(maps)); + + for (unsigned int i = 0; i < nr_maps; i++) { + if (map__load(maps_copy[i]) < 0) { + pr_warning("Failed to load map %s\n", dso__name(map__dso(maps_copy[i]))); + err = -1; + } + map__put(maps_copy[i]); + } + free(maps_copy); + return err; +} + void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data) { struct map **maps_by_address; @@ -667,40 +745,57 @@ struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp) return result; } -struct maps__find_symbol_by_name_args { - struct map **mapp; - const char *name; - struct symbol *sym; -}; - -static int maps__find_symbol_by_name_cb(struct map *map, void *data) +struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp) { - struct maps__find_symbol_by_name_args *args = data; + struct map **maps_copy; + unsigned int nr_maps; + struct symbol *sym = NULL; - args->sym = map__find_symbol_by_name(map, args->name); - if (!args->sym) - return 0; + if (!maps) + return NULL; - if (!map__contains_symbol(map, args->sym)) { - args->sym = NULL; - return 0; + /* + * First, ensure all maps are loaded. We pre-load them outside of any + * read-to-write locks to avoid deadlocks. Even if some fail, we proceed. + */ + maps__load_maps(maps); + + /* + * Create a local snapshot of the maps while holding the read lock. + * This prevents deadlocking if iteration triggers further map insertions. + */ + down_read(maps__lock(maps)); + nr_maps = maps__nr_maps(maps); + maps_copy = calloc(nr_maps, sizeof(*maps_copy)); + if (maps_copy) { + for (unsigned int i = 0; i < nr_maps; i++) { + struct map *map = maps__maps_by_address(maps)[i]; + + maps_copy[i] = map__get(map); + } } + up_read(maps__lock(maps)); - if (args->mapp != NULL) - *args->mapp = map__get(map); - return 1; -} + if (!maps_copy) + return NULL; -struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp) -{ - struct maps__find_symbol_by_name_args args = { - .mapp = mapp, - .name = name, - .sym = NULL, - }; + for (unsigned int i = 0; i < nr_maps; i++) { + struct map *map = maps_copy[i]; + + sym = map__find_symbol_by_name(map, name); + if (sym && map__contains_symbol(map, sym)) { + if (mapp) + *mapp = map__get(map); + break; + } + sym = NULL; + } - maps__for_each_map(maps, maps__find_symbol_by_name_cb, &args); - return args.sym; + for (unsigned int i = 0; i < nr_maps; i++) + map__put(maps_copy[i]); + + free(maps_copy); + return sym; } int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams) @@ -844,7 +939,6 @@ static int __maps__insert_sorted(struct maps *maps, unsigned int first_after_ind static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) { int err = 0; - FILE *fp = debug_file(); unsigned int i, ni = INT_MAX; // Some gcc complain, but depends on maps_by_name... if (!maps__maps_by_address_sorted(maps)) @@ -872,8 +966,8 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) dso__name(map__dso(new))); } else if (verbose >= 2) { pr_debug("overlapping maps:\n"); - map__fprintf(new, fp); - map__fprintf(pos, fp); + map__fprintf(new, debug_file()); + map__fprintf(pos, debug_file()); } if (maps_by_name) @@ -894,7 +988,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) map__set_end(before, map__start(new)); if (verbose >= 2 && !use_browser) - map__fprintf(before, fp); + map__fprintf(before, debug_file()); } if (map__end(new) < map__end(pos)) { /* The new map isn't as long as the existing map. */ @@ -912,7 +1006,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) map__map_ip(after, map__end(new))); if (verbose >= 2 && !use_browser) - map__fprintf(after, fp); + map__fprintf(after, debug_file()); } /* * If adding one entry, for `before` or `after`, we can replace @@ -956,6 +1050,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) if (maps_by_name) { map__put(maps_by_name[ni]); maps_by_name[ni] = map__get(new); + maps__set_maps_by_name_sorted(maps, false); } err = __maps__insert_sorted(maps, i + 1, after, NULL); @@ -982,6 +1077,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) if (maps_by_name) { map__put(maps_by_name[ni]); maps_by_name[ni] = map__get(new); + maps__set_maps_by_name_sorted(maps, false); } check_invariants(maps); @@ -1038,6 +1134,14 @@ int maps__copy_from(struct maps *dest, struct maps *parent) down_write(maps__lock(dest)); down_read(maps__lock(parent)); +#ifdef HAVE_LIBUNWIND_SUPPORT + err = unwind__prepare_access(dest, maps__e_machine(parent)); + if (err) { + up_read(maps__lock(parent)); + up_write(maps__lock(dest)); + return err; + } +#endif parent_maps_by_address = maps__maps_by_address(parent); n = maps__nr_maps(parent); if (maps__nr_maps(dest) == 0) { @@ -1067,29 +1171,19 @@ int maps__copy_from(struct maps *dest, struct maps *parent) if (!new) err = -ENOMEM; else { - err = unwind__prepare_access(dest, new, NULL); - if (!err) { - dest_maps_by_address[i] = new; - map__set_kmap_maps(new, dest); - if (dest_maps_by_name) - dest_maps_by_name[i] = map__get(new); - RC_CHK_ACCESS(dest)->nr_maps = i + 1; - } + dest_maps_by_address[i] = new; + map__set_kmap_maps(new, dest); + if (dest_maps_by_name) + dest_maps_by_name[i] = map__get(new); + RC_CHK_ACCESS(dest)->nr_maps = i + 1; } if (err) map__put(new); } maps__set_maps_by_address_sorted(dest, maps__maps_by_address_sorted(parent)); - if (!err) { - RC_CHK_ACCESS(dest)->last_search_by_name_idx = - RC_CHK_ACCESS(parent)->last_search_by_name_idx; - maps__set_maps_by_name_sorted(dest, - dest_maps_by_name && - maps__maps_by_name_sorted(parent)); - } else { - RC_CHK_ACCESS(dest)->last_search_by_name_idx = 0; - maps__set_maps_by_name_sorted(dest, false); - } + RC_CHK_ACCESS(dest)->last_search_by_name_idx = 0; + /* Values were copied into the name array in address order. */ + maps__set_maps_by_name_sorted(dest, false); } else { /* Unexpected copying to a maps containing entries. */ for (unsigned int i = 0; !err && i < n; i++) { @@ -1099,9 +1193,7 @@ int maps__copy_from(struct maps *dest, struct maps *parent) if (!new) err = -ENOMEM; else { - err = unwind__prepare_access(dest, new, NULL); - if (!err) - err = __maps__insert(dest, new); + err = __maps__insert(dest, new); } map__put(new); } |
