summaryrefslogtreecommitdiff
path: root/tools/perf/util/maps.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/maps.c')
-rw-r--r--tools/perf/util/maps.c206
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);
}