diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index d0899df77baa..67e0f81416cb 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -573,20 +573,63 @@ void map_groups__init(struct map_groups *mg, struct machine *machine) maps__init(&mg->maps); mg->machine = machine; mg->last_search_by_name = NULL; + mg->nr_maps = 0; + mg->maps_by_name = NULL; refcount_set(&mg->refcnt, 1); } +static void __map_groups__free_maps_by_name(struct map_groups *mg) +{ + /* + * Free everything to try to do it from the rbtree in the next search + */ + zfree(&mg->maps_by_name); + mg->nr_maps_allocated = 0; +} + void map_groups__insert(struct map_groups *mg, struct map *map) { - maps__insert(&mg->maps, map); + struct maps *maps = &mg->maps; + + down_write(&maps->lock); + __maps__insert(maps, map); + ++mg->nr_maps; + + /* + * If we already performed some search by name, then we need to add the just + * inserted map and resort. + */ + if (mg->maps_by_name) { + if (mg->nr_maps > mg->nr_maps_allocated) { + int nr_allocate = mg->nr_maps * 2; + struct map **maps_by_name = realloc(mg->maps_by_name, nr_allocate * sizeof(map)); + + if (maps_by_name == NULL) { + __map_groups__free_maps_by_name(mg); + return; + } + + mg->maps_by_name = maps_by_name; + mg->nr_maps_allocated = nr_allocate; + } + mg->maps_by_name[mg->nr_maps - 1] = map; + __map_groups__sort_by_name(mg); + } + up_write(&maps->lock); } void map_groups__remove(struct map_groups *mg, struct map *map) { + struct maps *maps = &mg->maps; + down_write(&maps->lock); if (mg->last_search_by_name == map) mg->last_search_by_name = NULL; - maps__remove(&mg->maps, map); + __maps__remove(maps, map); + --mg->nr_maps; + if (mg->maps_by_name) + __map_groups__free_maps_by_name(mg); + up_write(&maps->lock); } static void __maps__purge(struct maps *maps) @@ -904,7 +947,7 @@ void maps__insert(struct maps *maps, struct map *map) up_write(&maps->lock); } -static void __maps__remove(struct maps *maps, struct map *map) +void __maps__remove(struct maps *maps, struct map *map) { rb_erase_init(&map->rb_node, &maps->entries); map__put(map); diff --git a/tools/perf/util/map_groups.h b/tools/perf/util/map_groups.h index f2a3158572eb..63ed211fe241 100644 --- a/tools/perf/util/map_groups.h +++ b/tools/perf/util/map_groups.h @@ -21,6 +21,7 @@ struct maps { void maps__insert(struct maps *maps, struct map *map); void maps__remove(struct maps *maps, struct map *map); +void __maps__remove(struct maps *maps, struct map *map); struct map *maps__find(struct maps *maps, u64 addr); struct map *maps__first(struct maps *maps); struct map *map__next(struct map *map); @@ -37,7 +38,10 @@ struct map_groups { struct maps maps; struct machine *machine; struct map *last_search_by_name; + struct map **maps_by_name; refcount_t refcnt; + unsigned int nr_maps; + unsigned int nr_maps_allocated; #ifdef HAVE_LIBUNWIND_SUPPORT void *addr_space; struct unwind_libunwind_ops *unwind_libunwind_ops; @@ -97,4 +101,6 @@ struct map *map_groups__find_by_name(struct map_groups *mg, const char *name); int map_groups__merge_in(struct map_groups *kmaps, struct map *new_map); +void __map_groups__sort_by_name(struct map_groups *mg); + #endif // __PERF_MAP_GROUPS_H diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b5ae82a11d4b..db9667aacb88 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1760,6 +1760,56 @@ out: return ret; } +static int map__strcmp(const void *a, const void *b) +{ + const struct map *ma = *(const struct map **)a, *mb = *(const struct map **)b; + return strcmp(ma->dso->short_name, mb->dso->short_name); +} + +static int map__strcmp_name(const void *name, const void *b) +{ + const struct map *map = *(const struct map **)b; + return strcmp(name, map->dso->short_name); +} + +void __map_groups__sort_by_name(struct map_groups *mg) +{ + qsort(mg->maps_by_name, mg->nr_maps, sizeof(struct map *), map__strcmp); +} + +static int map__groups__sort_by_name_from_rbtree(struct map_groups *mg) +{ + struct map *map; + struct map **maps_by_name = realloc(mg->maps_by_name, mg->nr_maps * sizeof(map)); + int i = 0; + + if (maps_by_name == NULL) + return -1; + + mg->maps_by_name = maps_by_name; + mg->nr_maps_allocated = mg->nr_maps; + + maps__for_each_entry(&mg->maps, map) + maps_by_name[i++] = map; + + __map_groups__sort_by_name(mg); + return 0; +} + +static struct map *__map_groups__find_by_name(struct map_groups *mg, const char *name) +{ + struct map **mapp; + + if (mg->maps_by_name == NULL && + map__groups__sort_by_name_from_rbtree(mg)) + return NULL; + + mapp = bsearch(name, mg->maps_by_name, mg->nr_maps, sizeof(*mapp), map__strcmp_name); + if (mapp) + return *mapp; + return NULL; +} + struct map *map_groups__find_by_name(struct map_groups *mg, const char *name) { struct maps *maps = &mg->maps; @@ -1771,7 +1821,16 @@ struct map *map_groups__find_by_name(struct map_groups *mg, const char *name) map = mg->last_search_by_name; goto out_unlock; } + /* + * If we have mg->maps_by_name, then the name isn't in the rbtree, + * as mg->maps_by_name mirrors the rbtree when lookups by name are + * made. + */ + map = __map_groups__find_by_name(mg, name); + if (map || mg->maps_by_name != NULL) + goto out_unlock; + /* Fallback to traversing the rbtree... */ maps__for_each_entry(maps, map) if (strcmp(map->dso->short_name, name) == 0) { mg->last_search_by_name = map;