mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	If I build perf with asan and run Zstd test: $ make -C tools/perf O=/tmp/perf DEBUG=1 EXTRA_CFLAGS="-O0 -g -fno-omit-frame-pointer -fsanitize=undefined" $ /tmp/perf/perf test "Zstd perf.data compression/decompression" -vv 83: Zstd perf.data compression/decompression: ... util/maps.c:1046:5: runtime error: null pointer passed as argument 2, which is declared to never be null ... The issue was caused by `bsearch`. The patch adds a check to ensure argument 2 and 3 are not NULL and 0. Testing with the commands above confirms that the runtime error is resolved. Reviewed-by: Ian Rogers <irogers@google.com> Signed-off-by: Chun-Tse Shao <ctshao@google.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Ben Gainey <ben.gainey@arm.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Ingo Molnar <mingo@redhat.com> Cc: James Clark <james.clark@linaro.org> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Leo Yan <leo.yan@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Nick Terrell <terrelln@fb.com> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://lore.kernel.org/r/20250303183646.327510-2-ctshao@google.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
		
			
				
	
	
		
			1324 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1324 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
#include <errno.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <linux/zalloc.h>
 | 
						|
#include "debug.h"
 | 
						|
#include "dso.h"
 | 
						|
#include "map.h"
 | 
						|
#include "maps.h"
 | 
						|
#include "rwsem.h"
 | 
						|
#include "thread.h"
 | 
						|
#include "ui/ui.h"
 | 
						|
#include "unwind.h"
 | 
						|
#include <internal/rc_check.h>
 | 
						|
 | 
						|
/*
 | 
						|
 * Locking/sorting note:
 | 
						|
 *
 | 
						|
 * Sorting is done with the write lock, iteration and binary searching happens
 | 
						|
 * under the read lock requiring being sorted. There is a race between sorting
 | 
						|
 * releasing the write lock and acquiring the read lock for iteration/searching
 | 
						|
 * where another thread could insert and break the sorting of the maps. In
 | 
						|
 * practice inserting maps should be rare meaning that the race shouldn't lead
 | 
						|
 * to live lock. Removal of maps doesn't break being sorted.
 | 
						|
 */
 | 
						|
 | 
						|
DECLARE_RC_STRUCT(maps) {
 | 
						|
	struct rw_semaphore lock;
 | 
						|
	/**
 | 
						|
	 * @maps_by_address: array of maps sorted by their starting address if
 | 
						|
	 * maps_by_address_sorted is true.
 | 
						|
	 */
 | 
						|
	struct map	 **maps_by_address;
 | 
						|
	/**
 | 
						|
	 * @maps_by_name: optional array of maps sorted by their dso name if
 | 
						|
	 * maps_by_name_sorted is true.
 | 
						|
	 */
 | 
						|
	struct map	 **maps_by_name;
 | 
						|
	struct machine	 *machine;
 | 
						|
#ifdef HAVE_LIBUNWIND_SUPPORT
 | 
						|
	void		*addr_space;
 | 
						|
	const struct unwind_libunwind_ops *unwind_libunwind_ops;
 | 
						|
#endif
 | 
						|
	refcount_t	 refcnt;
 | 
						|
	/**
 | 
						|
	 * @nr_maps: number of maps_by_address, and possibly maps_by_name,
 | 
						|
	 * entries that contain maps.
 | 
						|
	 */
 | 
						|
	unsigned int	 nr_maps;
 | 
						|
	/**
 | 
						|
	 * @nr_maps_allocated: number of entries in maps_by_address and possibly
 | 
						|
	 * maps_by_name.
 | 
						|
	 */
 | 
						|
	unsigned int	 nr_maps_allocated;
 | 
						|
	/**
 | 
						|
	 * @last_search_by_name_idx: cache of last found by name entry's index
 | 
						|
	 * as frequent searches for the same dso name are common.
 | 
						|
	 */
 | 
						|
	unsigned int	 last_search_by_name_idx;
 | 
						|
	/** @maps_by_address_sorted: is maps_by_address sorted. */
 | 
						|
	bool		 maps_by_address_sorted;
 | 
						|
	/** @maps_by_name_sorted: is maps_by_name sorted. */
 | 
						|
	bool		 maps_by_name_sorted;
 | 
						|
	/** @ends_broken: does the map contain a map where end values are unset/unsorted? */
 | 
						|
	bool		 ends_broken;
 | 
						|
};
 | 
						|
 | 
						|
static void check_invariants(const struct maps *maps __maybe_unused)
 | 
						|
{
 | 
						|
#ifndef NDEBUG
 | 
						|
	assert(RC_CHK_ACCESS(maps)->nr_maps <= RC_CHK_ACCESS(maps)->nr_maps_allocated);
 | 
						|
	for (unsigned int i = 0; i < RC_CHK_ACCESS(maps)->nr_maps; i++) {
 | 
						|
		struct map *map = RC_CHK_ACCESS(maps)->maps_by_address[i];
 | 
						|
 | 
						|
		/* Check map is well-formed. */
 | 
						|
		assert(map__end(map) == 0 || map__start(map) <= map__end(map));
 | 
						|
		/* Expect at least 1 reference count. */
 | 
						|
		assert(refcount_read(map__refcnt(map)) > 0);
 | 
						|
 | 
						|
		if (map__dso(map) && dso__kernel(map__dso(map)))
 | 
						|
			assert(RC_CHK_EQUAL(map__kmap(map)->kmaps, maps));
 | 
						|
 | 
						|
		if (i > 0) {
 | 
						|
			struct map *prev = RC_CHK_ACCESS(maps)->maps_by_address[i - 1];
 | 
						|
 | 
						|
			/* If addresses are sorted... */
 | 
						|
			if (RC_CHK_ACCESS(maps)->maps_by_address_sorted) {
 | 
						|
				/* Maps should be in start address order. */
 | 
						|
				assert(map__start(prev) <= map__start(map));
 | 
						|
				/*
 | 
						|
				 * If the ends of maps aren't broken (during
 | 
						|
				 * construction) then they should be ordered
 | 
						|
				 * too.
 | 
						|
				 */
 | 
						|
				if (!RC_CHK_ACCESS(maps)->ends_broken) {
 | 
						|
					assert(map__end(prev) <= map__end(map));
 | 
						|
					assert(map__end(prev) <= map__start(map) ||
 | 
						|
					       map__start(prev) == map__start(map));
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (RC_CHK_ACCESS(maps)->maps_by_name) {
 | 
						|
		for (unsigned int i = 0; i < RC_CHK_ACCESS(maps)->nr_maps; i++) {
 | 
						|
			struct map *map = RC_CHK_ACCESS(maps)->maps_by_name[i];
 | 
						|
 | 
						|
			/*
 | 
						|
			 * Maps by name maps should be in maps_by_address, so
 | 
						|
			 * the reference count should be higher.
 | 
						|
			 */
 | 
						|
			assert(refcount_read(map__refcnt(map)) > 1);
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static struct map **maps__maps_by_address(const struct maps *maps)
 | 
						|
{
 | 
						|
	return RC_CHK_ACCESS(maps)->maps_by_address;
 | 
						|
}
 | 
						|
 | 
						|
static void maps__set_maps_by_address(struct maps *maps, struct map **new)
 | 
						|
{
 | 
						|
	RC_CHK_ACCESS(maps)->maps_by_address = new;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static void maps__set_nr_maps_allocated(struct maps *maps, unsigned int nr_maps_allocated)
 | 
						|
{
 | 
						|
	RC_CHK_ACCESS(maps)->nr_maps_allocated = nr_maps_allocated;
 | 
						|
}
 | 
						|
 | 
						|
static void maps__set_nr_maps(struct maps *maps, unsigned int nr_maps)
 | 
						|
{
 | 
						|
	RC_CHK_ACCESS(maps)->nr_maps = nr_maps;
 | 
						|
}
 | 
						|
 | 
						|
/* Not in the header, to aid reference counting. */
 | 
						|
static struct map **maps__maps_by_name(const struct maps *maps)
 | 
						|
{
 | 
						|
	return RC_CHK_ACCESS(maps)->maps_by_name;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static void maps__set_maps_by_name(struct maps *maps, struct map **new)
 | 
						|
{
 | 
						|
	RC_CHK_ACCESS(maps)->maps_by_name = new;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static bool maps__maps_by_address_sorted(const struct maps *maps)
 | 
						|
{
 | 
						|
	return RC_CHK_ACCESS(maps)->maps_by_address_sorted;
 | 
						|
}
 | 
						|
 | 
						|
static void maps__set_maps_by_address_sorted(struct maps *maps, bool value)
 | 
						|
{
 | 
						|
	RC_CHK_ACCESS(maps)->maps_by_address_sorted = value;
 | 
						|
}
 | 
						|
 | 
						|
static bool maps__maps_by_name_sorted(const struct maps *maps)
 | 
						|
{
 | 
						|
	return RC_CHK_ACCESS(maps)->maps_by_name_sorted;
 | 
						|
}
 | 
						|
 | 
						|
static void maps__set_maps_by_name_sorted(struct maps *maps, bool value)
 | 
						|
{
 | 
						|
	RC_CHK_ACCESS(maps)->maps_by_name_sorted = value;
 | 
						|
}
 | 
						|
 | 
						|
struct machine *maps__machine(const struct maps *maps)
 | 
						|
{
 | 
						|
	return RC_CHK_ACCESS(maps)->machine;
 | 
						|
}
 | 
						|
 | 
						|
unsigned int maps__nr_maps(const struct maps *maps)
 | 
						|
{
 | 
						|
	return RC_CHK_ACCESS(maps)->nr_maps;
 | 
						|
}
 | 
						|
 | 
						|
refcount_t *maps__refcnt(struct maps *maps)
 | 
						|
{
 | 
						|
	return &RC_CHK_ACCESS(maps)->refcnt;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_LIBUNWIND_SUPPORT
 | 
						|
void *maps__addr_space(const struct maps *maps)
 | 
						|
{
 | 
						|
	return RC_CHK_ACCESS(maps)->addr_space;
 | 
						|
}
 | 
						|
 | 
						|
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)
 | 
						|
{
 | 
						|
	return RC_CHK_ACCESS(maps)->unwind_libunwind_ops;
 | 
						|
}
 | 
						|
 | 
						|
void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops)
 | 
						|
{
 | 
						|
	RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static struct rw_semaphore *maps__lock(struct maps *maps)
 | 
						|
{
 | 
						|
	return &RC_CHK_ACCESS(maps)->lock;
 | 
						|
}
 | 
						|
 | 
						|
static void maps__init(struct maps *maps, struct machine *machine)
 | 
						|
{
 | 
						|
	init_rwsem(maps__lock(maps));
 | 
						|
	RC_CHK_ACCESS(maps)->maps_by_address = NULL;
 | 
						|
	RC_CHK_ACCESS(maps)->maps_by_name = NULL;
 | 
						|
	RC_CHK_ACCESS(maps)->machine = machine;
 | 
						|
#ifdef HAVE_LIBUNWIND_SUPPORT
 | 
						|
	RC_CHK_ACCESS(maps)->addr_space = NULL;
 | 
						|
	RC_CHK_ACCESS(maps)->unwind_libunwind_ops = NULL;
 | 
						|
#endif
 | 
						|
	refcount_set(maps__refcnt(maps), 1);
 | 
						|
	RC_CHK_ACCESS(maps)->nr_maps = 0;
 | 
						|
	RC_CHK_ACCESS(maps)->nr_maps_allocated = 0;
 | 
						|
	RC_CHK_ACCESS(maps)->last_search_by_name_idx = 0;
 | 
						|
	RC_CHK_ACCESS(maps)->maps_by_address_sorted = true;
 | 
						|
	RC_CHK_ACCESS(maps)->maps_by_name_sorted = false;
 | 
						|
}
 | 
						|
 | 
						|
static void maps__exit(struct maps *maps)
 | 
						|
{
 | 
						|
	struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
	struct map **maps_by_name = maps__maps_by_name(maps);
 | 
						|
 | 
						|
	for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
 | 
						|
		map__zput(maps_by_address[i]);
 | 
						|
		if (maps_by_name)
 | 
						|
			map__zput(maps_by_name[i]);
 | 
						|
	}
 | 
						|
	zfree(&maps_by_address);
 | 
						|
	zfree(&maps_by_name);
 | 
						|
	unwind__finish_access(maps);
 | 
						|
}
 | 
						|
 | 
						|
struct maps *maps__new(struct machine *machine)
 | 
						|
{
 | 
						|
	struct maps *result;
 | 
						|
	RC_STRUCT(maps) *maps = zalloc(sizeof(*maps));
 | 
						|
 | 
						|
	if (ADD_RC_CHK(result, maps))
 | 
						|
		maps__init(result, machine);
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
static void maps__delete(struct maps *maps)
 | 
						|
{
 | 
						|
	maps__exit(maps);
 | 
						|
	RC_CHK_FREE(maps);
 | 
						|
}
 | 
						|
 | 
						|
struct maps *maps__get(struct maps *maps)
 | 
						|
{
 | 
						|
	struct maps *result;
 | 
						|
 | 
						|
	if (RC_CHK_GET(result, maps))
 | 
						|
		refcount_inc(maps__refcnt(maps));
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
void maps__put(struct maps *maps)
 | 
						|
{
 | 
						|
	if (maps && refcount_dec_and_test(maps__refcnt(maps)))
 | 
						|
		maps__delete(maps);
 | 
						|
	else
 | 
						|
		RC_CHK_PUT(maps);
 | 
						|
}
 | 
						|
 | 
						|
static void __maps__free_maps_by_name(struct maps *maps)
 | 
						|
{
 | 
						|
	if (!maps__maps_by_name(maps))
 | 
						|
		return;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Free everything to try to do it from the rbtree in the next search
 | 
						|
	 */
 | 
						|
	for (unsigned int i = 0; i < maps__nr_maps(maps); i++)
 | 
						|
		map__put(maps__maps_by_name(maps)[i]);
 | 
						|
 | 
						|
	zfree(&RC_CHK_ACCESS(maps)->maps_by_name);
 | 
						|
 | 
						|
	/* Consistent with maps__init(). When maps_by_name == NULL, maps_by_name_sorted == false */
 | 
						|
	maps__set_maps_by_name_sorted(maps, false);
 | 
						|
}
 | 
						|
 | 
						|
static int map__start_cmp(const void *a, const void *b)
 | 
						|
{
 | 
						|
	const struct map *map_a = *(const struct map * const *)a;
 | 
						|
	const struct map *map_b = *(const struct map * const *)b;
 | 
						|
	u64 map_a_start = map__start(map_a);
 | 
						|
	u64 map_b_start = map__start(map_b);
 | 
						|
 | 
						|
	if (map_a_start == map_b_start) {
 | 
						|
		u64 map_a_end = map__end(map_a);
 | 
						|
		u64 map_b_end = map__end(map_b);
 | 
						|
 | 
						|
		if  (map_a_end == map_b_end) {
 | 
						|
			/* Ensure maps with the same addresses have a fixed order. */
 | 
						|
			if (RC_CHK_ACCESS(map_a) == RC_CHK_ACCESS(map_b))
 | 
						|
				return 0;
 | 
						|
			return (intptr_t)RC_CHK_ACCESS(map_a) > (intptr_t)RC_CHK_ACCESS(map_b)
 | 
						|
				? 1 : -1;
 | 
						|
		}
 | 
						|
		return map_a_end > map_b_end ? 1 : -1;
 | 
						|
	}
 | 
						|
	return map_a_start > map_b_start ? 1 : -1;
 | 
						|
}
 | 
						|
 | 
						|
static void __maps__sort_by_address(struct maps *maps)
 | 
						|
{
 | 
						|
	if (maps__maps_by_address_sorted(maps))
 | 
						|
		return;
 | 
						|
 | 
						|
	qsort(maps__maps_by_address(maps),
 | 
						|
		maps__nr_maps(maps),
 | 
						|
		sizeof(struct map *),
 | 
						|
		map__start_cmp);
 | 
						|
	maps__set_maps_by_address_sorted(maps, true);
 | 
						|
}
 | 
						|
 | 
						|
static void maps__sort_by_address(struct maps *maps)
 | 
						|
{
 | 
						|
	down_write(maps__lock(maps));
 | 
						|
	__maps__sort_by_address(maps);
 | 
						|
	up_write(maps__lock(maps));
 | 
						|
}
 | 
						|
 | 
						|
static int map__strcmp(const void *a, const void *b)
 | 
						|
{
 | 
						|
	const struct map *map_a = *(const struct map * const *)a;
 | 
						|
	const struct map *map_b = *(const struct map * const *)b;
 | 
						|
	const struct dso *dso_a = map__dso(map_a);
 | 
						|
	const struct dso *dso_b = map__dso(map_b);
 | 
						|
	int ret = strcmp(dso__short_name(dso_a), dso__short_name(dso_b));
 | 
						|
 | 
						|
	if (ret == 0 && RC_CHK_ACCESS(map_a) != RC_CHK_ACCESS(map_b)) {
 | 
						|
		/* Ensure distinct but name equal maps have an order. */
 | 
						|
		return map__start_cmp(a, b);
 | 
						|
	}
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int maps__sort_by_name(struct maps *maps)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	down_write(maps__lock(maps));
 | 
						|
	if (!maps__maps_by_name_sorted(maps)) {
 | 
						|
		struct map **maps_by_name = maps__maps_by_name(maps);
 | 
						|
 | 
						|
		if (!maps_by_name) {
 | 
						|
			maps_by_name = malloc(RC_CHK_ACCESS(maps)->nr_maps_allocated *
 | 
						|
					sizeof(*maps_by_name));
 | 
						|
			if (!maps_by_name)
 | 
						|
				err = -ENOMEM;
 | 
						|
			else {
 | 
						|
				struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
				unsigned int n = maps__nr_maps(maps);
 | 
						|
 | 
						|
				maps__set_maps_by_name(maps, maps_by_name);
 | 
						|
				for (unsigned int i = 0; i < n; i++)
 | 
						|
					maps_by_name[i] = map__get(maps_by_address[i]);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (!err) {
 | 
						|
			qsort(maps_by_name,
 | 
						|
				maps__nr_maps(maps),
 | 
						|
				sizeof(struct map *),
 | 
						|
				map__strcmp);
 | 
						|
			maps__set_maps_by_name_sorted(maps, true);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	check_invariants(maps);
 | 
						|
	up_write(maps__lock(maps));
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int maps__by_address_index(const struct maps *maps, const struct map *map)
 | 
						|
{
 | 
						|
	struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
 | 
						|
	if (maps__maps_by_address_sorted(maps)) {
 | 
						|
		struct map **mapp =
 | 
						|
			bsearch(&map, maps__maps_by_address(maps), maps__nr_maps(maps),
 | 
						|
				sizeof(*mapp), map__start_cmp);
 | 
						|
 | 
						|
		if (mapp)
 | 
						|
			return mapp - maps_by_address;
 | 
						|
	} else {
 | 
						|
		for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
 | 
						|
			if (RC_CHK_ACCESS(maps_by_address[i]) == RC_CHK_ACCESS(map))
 | 
						|
				return i;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	pr_err("Map missing from maps");
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int maps__by_name_index(const struct maps *maps, const struct map *map)
 | 
						|
{
 | 
						|
	struct map **maps_by_name = maps__maps_by_name(maps);
 | 
						|
 | 
						|
	if (maps__maps_by_name_sorted(maps)) {
 | 
						|
		struct map **mapp =
 | 
						|
			bsearch(&map, maps_by_name, maps__nr_maps(maps),
 | 
						|
				sizeof(*mapp), map__strcmp);
 | 
						|
 | 
						|
		if (mapp)
 | 
						|
			return mapp - maps_by_name;
 | 
						|
	} else {
 | 
						|
		for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
 | 
						|
			if (RC_CHK_ACCESS(maps_by_name[i]) == RC_CHK_ACCESS(map))
 | 
						|
				return i;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	pr_err("Map missing from maps");
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static void map__set_kmap_maps(struct map *map, struct maps *maps)
 | 
						|
{
 | 
						|
	struct dso *dso;
 | 
						|
 | 
						|
	if (map == NULL)
 | 
						|
		return;
 | 
						|
 | 
						|
	dso = map__dso(map);
 | 
						|
 | 
						|
	if (dso && dso__kernel(dso)) {
 | 
						|
                struct kmap *kmap = map__kmap(map);
 | 
						|
 | 
						|
                if (kmap)
 | 
						|
                        kmap->kmaps = maps;
 | 
						|
                else
 | 
						|
                        pr_err("Internal error: kernel dso with non kernel map\n");
 | 
						|
        }
 | 
						|
}
 | 
						|
 | 
						|
static int __maps__insert(struct maps *maps, struct map *new)
 | 
						|
{
 | 
						|
	struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
	struct map **maps_by_name = maps__maps_by_name(maps);
 | 
						|
	unsigned int nr_maps = maps__nr_maps(maps);
 | 
						|
	unsigned int nr_allocate = RC_CHK_ACCESS(maps)->nr_maps_allocated;
 | 
						|
 | 
						|
	if (nr_maps + 1 > nr_allocate) {
 | 
						|
		nr_allocate = !nr_allocate ? 32 : nr_allocate * 2;
 | 
						|
 | 
						|
		maps_by_address = realloc(maps_by_address, nr_allocate * sizeof(new));
 | 
						|
		if (!maps_by_address)
 | 
						|
			return -ENOMEM;
 | 
						|
 | 
						|
		maps__set_maps_by_address(maps, maps_by_address);
 | 
						|
		if (maps_by_name) {
 | 
						|
			maps_by_name = realloc(maps_by_name, nr_allocate * sizeof(new));
 | 
						|
			if (!maps_by_name) {
 | 
						|
				/*
 | 
						|
				 * If by name fails, just disable by name and it will
 | 
						|
				 * recompute next time it is required.
 | 
						|
				 */
 | 
						|
				__maps__free_maps_by_name(maps);
 | 
						|
			}
 | 
						|
			maps__set_maps_by_name(maps, maps_by_name);
 | 
						|
		}
 | 
						|
		RC_CHK_ACCESS(maps)->nr_maps_allocated = nr_allocate;
 | 
						|
	}
 | 
						|
	/* Insert the value at the end. */
 | 
						|
	maps_by_address[nr_maps] = map__get(new);
 | 
						|
	if (maps_by_name)
 | 
						|
		maps_by_name[nr_maps] = map__get(new);
 | 
						|
 | 
						|
	nr_maps++;
 | 
						|
	RC_CHK_ACCESS(maps)->nr_maps = nr_maps;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Recompute if things are sorted. If things are inserted in a sorted
 | 
						|
	 * manner, for example by processing /proc/pid/maps, then no
 | 
						|
	 * sorting/resorting will be necessary.
 | 
						|
	 */
 | 
						|
	if (nr_maps == 1) {
 | 
						|
		/* If there's just 1 entry then maps are sorted. */
 | 
						|
		maps__set_maps_by_address_sorted(maps, true);
 | 
						|
		maps__set_maps_by_name_sorted(maps, maps_by_name != NULL);
 | 
						|
	} else {
 | 
						|
		/* Sorted if maps were already sorted and this map starts after the last one. */
 | 
						|
		maps__set_maps_by_address_sorted(maps,
 | 
						|
			maps__maps_by_address_sorted(maps) &&
 | 
						|
			map__end(maps_by_address[nr_maps - 2]) <= map__start(new));
 | 
						|
		maps__set_maps_by_name_sorted(maps, false);
 | 
						|
	}
 | 
						|
	if (map__end(new) < map__start(new))
 | 
						|
		RC_CHK_ACCESS(maps)->ends_broken = true;
 | 
						|
 | 
						|
	map__set_kmap_maps(new, maps);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int maps__insert(struct maps *maps, struct map *map)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	down_write(maps__lock(maps));
 | 
						|
	ret = __maps__insert(maps, map);
 | 
						|
	check_invariants(maps);
 | 
						|
	up_write(maps__lock(maps));
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void __maps__remove(struct maps *maps, struct map *map)
 | 
						|
{
 | 
						|
	struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
	struct map **maps_by_name = maps__maps_by_name(maps);
 | 
						|
	unsigned int nr_maps = maps__nr_maps(maps);
 | 
						|
	unsigned int address_idx;
 | 
						|
 | 
						|
	/* Slide later mappings over the one to remove */
 | 
						|
	address_idx = maps__by_address_index(maps, map);
 | 
						|
	map__put(maps_by_address[address_idx]);
 | 
						|
	memmove(&maps_by_address[address_idx],
 | 
						|
		&maps_by_address[address_idx + 1],
 | 
						|
		(nr_maps - address_idx - 1) * sizeof(*maps_by_address));
 | 
						|
 | 
						|
	if (maps_by_name) {
 | 
						|
		unsigned int name_idx = maps__by_name_index(maps, map);
 | 
						|
 | 
						|
		map__put(maps_by_name[name_idx]);
 | 
						|
		memmove(&maps_by_name[name_idx],
 | 
						|
			&maps_by_name[name_idx + 1],
 | 
						|
			(nr_maps - name_idx - 1) *  sizeof(*maps_by_name));
 | 
						|
	}
 | 
						|
 | 
						|
	--RC_CHK_ACCESS(maps)->nr_maps;
 | 
						|
}
 | 
						|
 | 
						|
void maps__remove(struct maps *maps, struct map *map)
 | 
						|
{
 | 
						|
	down_write(maps__lock(maps));
 | 
						|
	__maps__remove(maps, map);
 | 
						|
	check_invariants(maps);
 | 
						|
	up_write(maps__lock(maps));
 | 
						|
}
 | 
						|
 | 
						|
bool maps__empty(struct maps *maps)
 | 
						|
{
 | 
						|
	bool res;
 | 
						|
 | 
						|
	down_read(maps__lock(maps));
 | 
						|
	res = maps__nr_maps(maps) == 0;
 | 
						|
	up_read(maps__lock(maps));
 | 
						|
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
bool maps__equal(struct maps *a, struct maps *b)
 | 
						|
{
 | 
						|
	return RC_CHK_EQUAL(a, b);
 | 
						|
}
 | 
						|
 | 
						|
int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data), void *data)
 | 
						|
{
 | 
						|
	bool done = false;
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	/* See locking/sorting note. */
 | 
						|
	while (!done) {
 | 
						|
		down_read(maps__lock(maps));
 | 
						|
		if (maps__maps_by_address_sorted(maps)) {
 | 
						|
			/*
 | 
						|
			 * maps__for_each_map callbacks may buggily/unsafely
 | 
						|
			 * insert into maps_by_address. Deliberately reload
 | 
						|
			 * maps__nr_maps and maps_by_address on each iteration
 | 
						|
			 * to avoid using memory freed by maps__insert growing
 | 
						|
			 * the array - this may cause maps to be skipped or
 | 
						|
			 * repeated.
 | 
						|
			 */
 | 
						|
			for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
 | 
						|
				struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
				struct map *map = maps_by_address[i];
 | 
						|
 | 
						|
				ret = cb(map, data);
 | 
						|
				if (ret)
 | 
						|
					break;
 | 
						|
			}
 | 
						|
			done = true;
 | 
						|
		}
 | 
						|
		up_read(maps__lock(maps));
 | 
						|
		if (!done)
 | 
						|
			maps__sort_by_address(maps);
 | 
						|
	}
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data)
 | 
						|
{
 | 
						|
	struct map **maps_by_address;
 | 
						|
 | 
						|
	down_write(maps__lock(maps));
 | 
						|
 | 
						|
	maps_by_address = maps__maps_by_address(maps);
 | 
						|
	for (unsigned int i = 0; i < maps__nr_maps(maps);) {
 | 
						|
		if (cb(maps_by_address[i], data))
 | 
						|
			__maps__remove(maps, maps_by_address[i]);
 | 
						|
		else
 | 
						|
			i++;
 | 
						|
	}
 | 
						|
	check_invariants(maps);
 | 
						|
	up_write(maps__lock(maps));
 | 
						|
}
 | 
						|
 | 
						|
struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp)
 | 
						|
{
 | 
						|
	struct map *map = maps__find(maps, addr);
 | 
						|
	struct symbol *result = NULL;
 | 
						|
 | 
						|
	/* Ensure map is loaded before using map->map_ip */
 | 
						|
	if (map != NULL && map__load(map) >= 0)
 | 
						|
		result = map__find_symbol(map, map__map_ip(map, addr));
 | 
						|
 | 
						|
	if (mapp)
 | 
						|
		*mapp = map;
 | 
						|
	else
 | 
						|
		map__put(map);
 | 
						|
 | 
						|
	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 maps__find_symbol_by_name_args *args = data;
 | 
						|
 | 
						|
	args->sym = map__find_symbol_by_name(map, args->name);
 | 
						|
	if (!args->sym)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (!map__contains_symbol(map, args->sym)) {
 | 
						|
		args->sym = NULL;
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if (args->mapp != NULL)
 | 
						|
		*args->mapp = map__get(map);
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
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,
 | 
						|
	};
 | 
						|
 | 
						|
	maps__for_each_map(maps, maps__find_symbol_by_name_cb, &args);
 | 
						|
	return args.sym;
 | 
						|
}
 | 
						|
 | 
						|
int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams)
 | 
						|
{
 | 
						|
	if (ams->addr < map__start(ams->ms.map) || ams->addr >= map__end(ams->ms.map)) {
 | 
						|
		if (maps == NULL)
 | 
						|
			return -1;
 | 
						|
		ams->ms.map = maps__find(maps, ams->addr);
 | 
						|
		if (ams->ms.map == NULL)
 | 
						|
			return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	ams->al_addr = map__map_ip(ams->ms.map, ams->addr);
 | 
						|
	ams->ms.sym = map__find_symbol(ams->ms.map, ams->al_addr);
 | 
						|
 | 
						|
	return ams->ms.sym ? 0 : -1;
 | 
						|
}
 | 
						|
 | 
						|
struct maps__fprintf_args {
 | 
						|
	FILE *fp;
 | 
						|
	size_t printed;
 | 
						|
};
 | 
						|
 | 
						|
static int maps__fprintf_cb(struct map *map, void *data)
 | 
						|
{
 | 
						|
	struct maps__fprintf_args *args = data;
 | 
						|
 | 
						|
	args->printed += fprintf(args->fp, "Map:");
 | 
						|
	args->printed += map__fprintf(map, args->fp);
 | 
						|
	if (verbose > 2) {
 | 
						|
		args->printed += dso__fprintf(map__dso(map), args->fp);
 | 
						|
		args->printed += fprintf(args->fp, "--\n");
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
size_t maps__fprintf(struct maps *maps, FILE *fp)
 | 
						|
{
 | 
						|
	struct maps__fprintf_args args = {
 | 
						|
		.fp = fp,
 | 
						|
		.printed = 0,
 | 
						|
	};
 | 
						|
 | 
						|
	maps__for_each_map(maps, maps__fprintf_cb, &args);
 | 
						|
 | 
						|
	return args.printed;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Find first map where end > map->start.
 | 
						|
 * Same as find_vma() in kernel.
 | 
						|
 */
 | 
						|
static unsigned int first_ending_after(struct maps *maps, const struct map *map)
 | 
						|
{
 | 
						|
	struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
	int low = 0, high = (int)maps__nr_maps(maps) - 1, first = high + 1;
 | 
						|
 | 
						|
	assert(maps__maps_by_address_sorted(maps));
 | 
						|
	if (low <= high && map__end(maps_by_address[0]) > map__start(map))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	while (low <= high) {
 | 
						|
		int mid = (low + high) / 2;
 | 
						|
		struct map *pos = maps_by_address[mid];
 | 
						|
 | 
						|
		if (map__end(pos) > map__start(map)) {
 | 
						|
			first = mid;
 | 
						|
			if (map__start(pos) <= map__start(map)) {
 | 
						|
				/* Entry overlaps map. */
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			high = mid - 1;
 | 
						|
		} else
 | 
						|
			low = mid + 1;
 | 
						|
	}
 | 
						|
	return first;
 | 
						|
}
 | 
						|
 | 
						|
static int __maps__insert_sorted(struct maps *maps, unsigned int first_after_index,
 | 
						|
				 struct map *new1, struct map *new2)
 | 
						|
{
 | 
						|
	struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
	struct map **maps_by_name = maps__maps_by_name(maps);
 | 
						|
	unsigned int nr_maps = maps__nr_maps(maps);
 | 
						|
	unsigned int nr_allocate = RC_CHK_ACCESS(maps)->nr_maps_allocated;
 | 
						|
	unsigned int to_add = new2 ? 2 : 1;
 | 
						|
 | 
						|
	assert(maps__maps_by_address_sorted(maps));
 | 
						|
	assert(first_after_index == nr_maps ||
 | 
						|
	       map__end(new1) <= map__start(maps_by_address[first_after_index]));
 | 
						|
	assert(!new2 || map__end(new1) <= map__start(new2));
 | 
						|
	assert(first_after_index == nr_maps || !new2 ||
 | 
						|
	       map__end(new2) <= map__start(maps_by_address[first_after_index]));
 | 
						|
 | 
						|
	if (nr_maps + to_add > nr_allocate) {
 | 
						|
		nr_allocate = !nr_allocate ? 32 : nr_allocate * 2;
 | 
						|
 | 
						|
		maps_by_address = realloc(maps_by_address, nr_allocate * sizeof(new1));
 | 
						|
		if (!maps_by_address)
 | 
						|
			return -ENOMEM;
 | 
						|
 | 
						|
		maps__set_maps_by_address(maps, maps_by_address);
 | 
						|
		if (maps_by_name) {
 | 
						|
			maps_by_name = realloc(maps_by_name, nr_allocate * sizeof(new1));
 | 
						|
			if (!maps_by_name) {
 | 
						|
				/*
 | 
						|
				 * If by name fails, just disable by name and it will
 | 
						|
				 * recompute next time it is required.
 | 
						|
				 */
 | 
						|
				__maps__free_maps_by_name(maps);
 | 
						|
			}
 | 
						|
			maps__set_maps_by_name(maps, maps_by_name);
 | 
						|
		}
 | 
						|
		RC_CHK_ACCESS(maps)->nr_maps_allocated = nr_allocate;
 | 
						|
	}
 | 
						|
	memmove(&maps_by_address[first_after_index+to_add],
 | 
						|
		&maps_by_address[first_after_index],
 | 
						|
		(nr_maps - first_after_index) * sizeof(new1));
 | 
						|
	maps_by_address[first_after_index] = map__get(new1);
 | 
						|
	if (maps_by_name)
 | 
						|
		maps_by_name[nr_maps] = map__get(new1);
 | 
						|
	if (new2) {
 | 
						|
		maps_by_address[first_after_index + 1] = map__get(new2);
 | 
						|
		if (maps_by_name)
 | 
						|
			maps_by_name[nr_maps + 1] = map__get(new2);
 | 
						|
	}
 | 
						|
	RC_CHK_ACCESS(maps)->nr_maps = nr_maps + to_add;
 | 
						|
	maps__set_maps_by_name_sorted(maps, false);
 | 
						|
	map__set_kmap_maps(new1, maps);
 | 
						|
	map__set_kmap_maps(new2, maps);
 | 
						|
 | 
						|
	check_invariants(maps);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Adds new to maps, if new overlaps existing entries then the existing maps are
 | 
						|
 * adjusted or removed so that new fits without overlapping any entries.
 | 
						|
 */
 | 
						|
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))
 | 
						|
		__maps__sort_by_address(maps);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Iterate through entries where the end of the existing entry is
 | 
						|
	 * greater-than the new map's start.
 | 
						|
	 */
 | 
						|
	for (i = first_ending_after(maps, new); i < maps__nr_maps(maps); ) {
 | 
						|
		struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
		struct map **maps_by_name = maps__maps_by_name(maps);
 | 
						|
		struct map *pos = maps_by_address[i];
 | 
						|
		struct map *before = NULL, *after = NULL;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Stop if current map starts after map->end.
 | 
						|
		 * Maps are ordered by start: next will not overlap for sure.
 | 
						|
		 */
 | 
						|
		if (map__start(pos) >= map__end(new))
 | 
						|
			break;
 | 
						|
 | 
						|
		if (use_browser) {
 | 
						|
			pr_debug("overlapping maps in %s (disable tui for more info)\n",
 | 
						|
				dso__name(map__dso(new)));
 | 
						|
		} else if (verbose >= 2) {
 | 
						|
			pr_debug("overlapping maps:\n");
 | 
						|
			map__fprintf(new, fp);
 | 
						|
			map__fprintf(pos, fp);
 | 
						|
		}
 | 
						|
 | 
						|
		if (maps_by_name)
 | 
						|
			ni = maps__by_name_index(maps, pos);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Now check if we need to create new maps for areas not
 | 
						|
		 * overlapped by the new map:
 | 
						|
		 */
 | 
						|
		if (map__start(new) > map__start(pos)) {
 | 
						|
			/* Map starts within existing map. Need to shorten the existing map. */
 | 
						|
			before = map__clone(pos);
 | 
						|
 | 
						|
			if (before == NULL) {
 | 
						|
				err = -ENOMEM;
 | 
						|
				goto out_err;
 | 
						|
			}
 | 
						|
			map__set_end(before, map__start(new));
 | 
						|
 | 
						|
			if (verbose >= 2 && !use_browser)
 | 
						|
				map__fprintf(before, fp);
 | 
						|
		}
 | 
						|
		if (map__end(new) < map__end(pos)) {
 | 
						|
			/* The new map isn't as long as the existing map. */
 | 
						|
			after = map__clone(pos);
 | 
						|
 | 
						|
			if (after == NULL) {
 | 
						|
				map__zput(before);
 | 
						|
				err = -ENOMEM;
 | 
						|
				goto out_err;
 | 
						|
			}
 | 
						|
 | 
						|
			map__set_start(after, map__end(new));
 | 
						|
			map__add_pgoff(after, map__end(new) - map__start(pos));
 | 
						|
			assert(map__map_ip(pos, map__end(new)) ==
 | 
						|
			       map__map_ip(after, map__end(new)));
 | 
						|
 | 
						|
			if (verbose >= 2 && !use_browser)
 | 
						|
				map__fprintf(after, fp);
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		 * If adding one entry, for `before` or `after`, we can replace
 | 
						|
		 * the existing entry. If both `before` and `after` are
 | 
						|
		 * necessary than an insert is needed. If the existing entry
 | 
						|
		 * entirely overlaps the existing entry it can just be removed.
 | 
						|
		 */
 | 
						|
		if (before) {
 | 
						|
			map__put(maps_by_address[i]);
 | 
						|
			maps_by_address[i] = before;
 | 
						|
 | 
						|
			if (maps_by_name) {
 | 
						|
				map__put(maps_by_name[ni]);
 | 
						|
				maps_by_name[ni] = map__get(before);
 | 
						|
			}
 | 
						|
 | 
						|
			/* Maps are still ordered, go to next one. */
 | 
						|
			i++;
 | 
						|
			if (after) {
 | 
						|
				/*
 | 
						|
				 * 'before' and 'after' mean 'new' split the
 | 
						|
				 * 'pos' mapping and therefore there are no
 | 
						|
				 * later mappings.
 | 
						|
				 */
 | 
						|
				err = __maps__insert_sorted(maps, i, new, after);
 | 
						|
				map__put(after);
 | 
						|
				check_invariants(maps);
 | 
						|
				return err;
 | 
						|
			}
 | 
						|
			check_invariants(maps);
 | 
						|
		} else if (after) {
 | 
						|
			/*
 | 
						|
			 * 'after' means 'new' split 'pos' and there are no
 | 
						|
			 * later mappings.
 | 
						|
			 */
 | 
						|
			map__put(maps_by_address[i]);
 | 
						|
			maps_by_address[i] = map__get(new);
 | 
						|
 | 
						|
			if (maps_by_name) {
 | 
						|
				map__put(maps_by_name[ni]);
 | 
						|
				maps_by_name[ni] = map__get(new);
 | 
						|
			}
 | 
						|
 | 
						|
			err = __maps__insert_sorted(maps, i + 1, after, NULL);
 | 
						|
			map__put(after);
 | 
						|
			check_invariants(maps);
 | 
						|
			return err;
 | 
						|
		} else {
 | 
						|
			struct map *next = NULL;
 | 
						|
 | 
						|
			if (i + 1 < maps__nr_maps(maps))
 | 
						|
				next = maps_by_address[i + 1];
 | 
						|
 | 
						|
			if (!next  || map__start(next) >= map__end(new)) {
 | 
						|
				/*
 | 
						|
				 * Replace existing mapping and end knowing
 | 
						|
				 * there aren't later overlapping or any
 | 
						|
				 * mappings.
 | 
						|
				 */
 | 
						|
				map__put(maps_by_address[i]);
 | 
						|
				maps_by_address[i] = map__get(new);
 | 
						|
 | 
						|
				if (maps_by_name) {
 | 
						|
					map__put(maps_by_name[ni]);
 | 
						|
					maps_by_name[ni] = map__get(new);
 | 
						|
				}
 | 
						|
 | 
						|
				map__set_kmap_maps(new, maps);
 | 
						|
 | 
						|
				check_invariants(maps);
 | 
						|
				return err;
 | 
						|
			}
 | 
						|
			__maps__remove(maps, pos);
 | 
						|
			check_invariants(maps);
 | 
						|
			/*
 | 
						|
			 * Maps are ordered but no need to increase `i` as the
 | 
						|
			 * later maps were moved down.
 | 
						|
			 */
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Add the map. */
 | 
						|
	err = __maps__insert_sorted(maps, i, new, NULL);
 | 
						|
out_err:
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
int maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	down_write(maps__lock(maps));
 | 
						|
	err =  __maps__fixup_overlap_and_insert(maps, new);
 | 
						|
	up_write(maps__lock(maps));
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
int maps__copy_from(struct maps *dest, struct maps *parent)
 | 
						|
{
 | 
						|
	/* Note, if struct map were immutable then cloning could use ref counts. */
 | 
						|
	struct map **parent_maps_by_address;
 | 
						|
	int err = 0;
 | 
						|
	unsigned int n;
 | 
						|
 | 
						|
	down_write(maps__lock(dest));
 | 
						|
	down_read(maps__lock(parent));
 | 
						|
 | 
						|
	parent_maps_by_address = maps__maps_by_address(parent);
 | 
						|
	n = maps__nr_maps(parent);
 | 
						|
	if (maps__nr_maps(dest) == 0) {
 | 
						|
		/* No existing mappings so just copy from parent to avoid reallocs in insert. */
 | 
						|
		unsigned int nr_maps_allocated = RC_CHK_ACCESS(parent)->nr_maps_allocated;
 | 
						|
		struct map **dest_maps_by_address =
 | 
						|
			malloc(nr_maps_allocated * sizeof(struct map *));
 | 
						|
		struct map **dest_maps_by_name = NULL;
 | 
						|
 | 
						|
		if (!dest_maps_by_address)
 | 
						|
			err = -ENOMEM;
 | 
						|
		else {
 | 
						|
			if (maps__maps_by_name(parent)) {
 | 
						|
				dest_maps_by_name =
 | 
						|
					malloc(nr_maps_allocated * sizeof(struct map *));
 | 
						|
			}
 | 
						|
 | 
						|
			RC_CHK_ACCESS(dest)->maps_by_address = dest_maps_by_address;
 | 
						|
			RC_CHK_ACCESS(dest)->maps_by_name = dest_maps_by_name;
 | 
						|
			RC_CHK_ACCESS(dest)->nr_maps_allocated = nr_maps_allocated;
 | 
						|
		}
 | 
						|
 | 
						|
		for (unsigned int i = 0; !err && i < n; i++) {
 | 
						|
			struct map *pos = parent_maps_by_address[i];
 | 
						|
			struct map *new = map__clone(pos);
 | 
						|
 | 
						|
			if (!new)
 | 
						|
				err = -ENOMEM;
 | 
						|
			else {
 | 
						|
				err = unwind__prepare_access(dest, new, NULL);
 | 
						|
				if (!err) {
 | 
						|
					dest_maps_by_address[i] = new;
 | 
						|
					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);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		/* Unexpected copying to a maps containing entries. */
 | 
						|
		for (unsigned int i = 0; !err && i < n; i++) {
 | 
						|
			struct map *pos = parent_maps_by_address[i];
 | 
						|
			struct map *new = map__clone(pos);
 | 
						|
 | 
						|
			if (!new)
 | 
						|
				err = -ENOMEM;
 | 
						|
			else {
 | 
						|
				err = unwind__prepare_access(dest, new, NULL);
 | 
						|
				if (!err)
 | 
						|
					err = __maps__insert(dest, new);
 | 
						|
			}
 | 
						|
			map__put(new);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	check_invariants(dest);
 | 
						|
 | 
						|
	up_read(maps__lock(parent));
 | 
						|
	up_write(maps__lock(dest));
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
static int map__addr_cmp(const void *key, const void *entry)
 | 
						|
{
 | 
						|
	const u64 ip = *(const u64 *)key;
 | 
						|
	const struct map *map = *(const struct map * const *)entry;
 | 
						|
 | 
						|
	if (ip < map__start(map))
 | 
						|
		return -1;
 | 
						|
	if (ip >= map__end(map))
 | 
						|
		return 1;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct map *maps__find(struct maps *maps, u64 ip)
 | 
						|
{
 | 
						|
	struct map *result = NULL;
 | 
						|
	bool done = false;
 | 
						|
 | 
						|
	/* See locking/sorting note. */
 | 
						|
	while (!done) {
 | 
						|
		down_read(maps__lock(maps));
 | 
						|
		if (maps__maps_by_address_sorted(maps)) {
 | 
						|
			struct map **mapp = NULL;
 | 
						|
			struct map **maps_by_address = maps__maps_by_address(maps);
 | 
						|
			unsigned int nr_maps = maps__nr_maps(maps);
 | 
						|
 | 
						|
			if (maps_by_address && nr_maps)
 | 
						|
				mapp = bsearch(&ip, maps_by_address, nr_maps, sizeof(*mapp),
 | 
						|
					       map__addr_cmp);
 | 
						|
			if (mapp)
 | 
						|
				result = map__get(*mapp);
 | 
						|
			done = true;
 | 
						|
		}
 | 
						|
		up_read(maps__lock(maps));
 | 
						|
		if (!done)
 | 
						|
			maps__sort_by_address(maps);
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
static int map__strcmp_name(const void *name, const void *b)
 | 
						|
{
 | 
						|
	const struct dso *dso = map__dso(*(const struct map **)b);
 | 
						|
 | 
						|
	return strcmp(name, dso__short_name(dso));
 | 
						|
}
 | 
						|
 | 
						|
struct map *maps__find_by_name(struct maps *maps, const char *name)
 | 
						|
{
 | 
						|
	struct map *result = NULL;
 | 
						|
	bool done = false;
 | 
						|
 | 
						|
	/* See locking/sorting note. */
 | 
						|
	while (!done) {
 | 
						|
		unsigned int i;
 | 
						|
 | 
						|
		down_read(maps__lock(maps));
 | 
						|
 | 
						|
		/* First check last found entry. */
 | 
						|
		i = RC_CHK_ACCESS(maps)->last_search_by_name_idx;
 | 
						|
		if (i < maps__nr_maps(maps) && maps__maps_by_name(maps)) {
 | 
						|
			struct dso *dso = map__dso(maps__maps_by_name(maps)[i]);
 | 
						|
 | 
						|
			if (dso && strcmp(dso__short_name(dso), name) == 0) {
 | 
						|
				result = map__get(maps__maps_by_name(maps)[i]);
 | 
						|
				done = true;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* Second search sorted array. */
 | 
						|
		if (!done && maps__maps_by_name_sorted(maps)) {
 | 
						|
			struct map **mapp =
 | 
						|
				bsearch(name, maps__maps_by_name(maps), maps__nr_maps(maps),
 | 
						|
					sizeof(*mapp), map__strcmp_name);
 | 
						|
 | 
						|
			if (mapp) {
 | 
						|
				result = map__get(*mapp);
 | 
						|
				i = mapp - maps__maps_by_name(maps);
 | 
						|
				RC_CHK_ACCESS(maps)->last_search_by_name_idx = i;
 | 
						|
			}
 | 
						|
			done = true;
 | 
						|
		}
 | 
						|
		up_read(maps__lock(maps));
 | 
						|
		if (!done) {
 | 
						|
			/* Sort and retry binary search. */
 | 
						|
			if (maps__sort_by_name(maps)) {
 | 
						|
				/*
 | 
						|
				 * Memory allocation failed do linear search
 | 
						|
				 * through address sorted maps.
 | 
						|
				 */
 | 
						|
				struct map **maps_by_address;
 | 
						|
				unsigned int n;
 | 
						|
 | 
						|
				down_read(maps__lock(maps));
 | 
						|
				maps_by_address =  maps__maps_by_address(maps);
 | 
						|
				n = maps__nr_maps(maps);
 | 
						|
				for (i = 0; i < n; i++) {
 | 
						|
					struct map *pos = maps_by_address[i];
 | 
						|
					struct dso *dso = map__dso(pos);
 | 
						|
 | 
						|
					if (dso && strcmp(dso__short_name(dso), name) == 0) {
 | 
						|
						result = map__get(pos);
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				up_read(maps__lock(maps));
 | 
						|
				done = true;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
struct map *maps__find_next_entry(struct maps *maps, struct map *map)
 | 
						|
{
 | 
						|
	unsigned int i;
 | 
						|
	struct map *result = NULL;
 | 
						|
 | 
						|
	down_read(maps__lock(maps));
 | 
						|
	while (!maps__maps_by_address_sorted(maps)) {
 | 
						|
		up_read(maps__lock(maps));
 | 
						|
		maps__sort_by_address(maps);
 | 
						|
		down_read(maps__lock(maps));
 | 
						|
	}
 | 
						|
	i = maps__by_address_index(maps, map);
 | 
						|
	if (++i < maps__nr_maps(maps))
 | 
						|
		result = map__get(maps__maps_by_address(maps)[i]);
 | 
						|
 | 
						|
	up_read(maps__lock(maps));
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
void maps__fixup_end(struct maps *maps)
 | 
						|
{
 | 
						|
	struct map **maps_by_address;
 | 
						|
	unsigned int n;
 | 
						|
 | 
						|
	down_write(maps__lock(maps));
 | 
						|
	if (!maps__maps_by_address_sorted(maps))
 | 
						|
		__maps__sort_by_address(maps);
 | 
						|
 | 
						|
	maps_by_address = maps__maps_by_address(maps);
 | 
						|
	n = maps__nr_maps(maps);
 | 
						|
	for (unsigned int i = 1; i < n; i++) {
 | 
						|
		struct map *prev = maps_by_address[i - 1];
 | 
						|
		struct map *curr = maps_by_address[i];
 | 
						|
 | 
						|
		if (!map__end(prev) || map__end(prev) > map__start(curr))
 | 
						|
			map__set_end(prev, map__start(curr));
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * We still haven't the actual symbols, so guess the
 | 
						|
	 * last map final address.
 | 
						|
	 */
 | 
						|
	if (n > 0 && !map__end(maps_by_address[n - 1]))
 | 
						|
		map__set_end(maps_by_address[n - 1], ~0ULL);
 | 
						|
 | 
						|
	RC_CHK_ACCESS(maps)->ends_broken = false;
 | 
						|
	check_invariants(maps);
 | 
						|
 | 
						|
	up_write(maps__lock(maps));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Merges map into maps by splitting the new map within the existing map
 | 
						|
 * regions.
 | 
						|
 */
 | 
						|
int maps__merge_in(struct maps *kmaps, struct map *new_map)
 | 
						|
{
 | 
						|
	unsigned int first_after_, kmaps__nr_maps;
 | 
						|
	struct map **kmaps_maps_by_address;
 | 
						|
	struct map **merged_maps_by_address;
 | 
						|
	unsigned int merged_nr_maps_allocated;
 | 
						|
 | 
						|
	/* First try under a read lock. */
 | 
						|
	while (true) {
 | 
						|
		down_read(maps__lock(kmaps));
 | 
						|
		if (maps__maps_by_address_sorted(kmaps))
 | 
						|
			break;
 | 
						|
 | 
						|
		up_read(maps__lock(kmaps));
 | 
						|
 | 
						|
		/* First after binary search requires sorted maps. Sort and try again. */
 | 
						|
		maps__sort_by_address(kmaps);
 | 
						|
	}
 | 
						|
	first_after_ = first_ending_after(kmaps, new_map);
 | 
						|
	kmaps_maps_by_address = maps__maps_by_address(kmaps);
 | 
						|
 | 
						|
	if (first_after_ >= maps__nr_maps(kmaps) ||
 | 
						|
	    map__start(kmaps_maps_by_address[first_after_]) >= map__end(new_map)) {
 | 
						|
		/* No overlap so regular insert suffices. */
 | 
						|
		up_read(maps__lock(kmaps));
 | 
						|
		return maps__insert(kmaps, new_map);
 | 
						|
	}
 | 
						|
	up_read(maps__lock(kmaps));
 | 
						|
 | 
						|
	/* Plain insert with a read-lock failed, try again now with the write lock. */
 | 
						|
	down_write(maps__lock(kmaps));
 | 
						|
	if (!maps__maps_by_address_sorted(kmaps))
 | 
						|
		__maps__sort_by_address(kmaps);
 | 
						|
 | 
						|
	first_after_ = first_ending_after(kmaps, new_map);
 | 
						|
	kmaps_maps_by_address = maps__maps_by_address(kmaps);
 | 
						|
	kmaps__nr_maps = maps__nr_maps(kmaps);
 | 
						|
 | 
						|
	if (first_after_ >= kmaps__nr_maps ||
 | 
						|
	    map__start(kmaps_maps_by_address[first_after_]) >= map__end(new_map)) {
 | 
						|
		/* No overlap so regular insert suffices. */
 | 
						|
		int ret = __maps__insert(kmaps, new_map);
 | 
						|
 | 
						|
		check_invariants(kmaps);
 | 
						|
		up_write(maps__lock(kmaps));
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
	/* Array to merge into, possibly 1 more for the sake of new_map. */
 | 
						|
	merged_nr_maps_allocated = RC_CHK_ACCESS(kmaps)->nr_maps_allocated;
 | 
						|
	if (kmaps__nr_maps + 1 == merged_nr_maps_allocated)
 | 
						|
		merged_nr_maps_allocated++;
 | 
						|
 | 
						|
	merged_maps_by_address = malloc(merged_nr_maps_allocated * sizeof(*merged_maps_by_address));
 | 
						|
	if (!merged_maps_by_address) {
 | 
						|
		up_write(maps__lock(kmaps));
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
	maps__set_maps_by_address(kmaps, merged_maps_by_address);
 | 
						|
	maps__set_maps_by_address_sorted(kmaps, true);
 | 
						|
	__maps__free_maps_by_name(kmaps);
 | 
						|
	maps__set_nr_maps_allocated(kmaps, merged_nr_maps_allocated);
 | 
						|
 | 
						|
	/* Copy entries before the new_map that can't overlap. */
 | 
						|
	for (unsigned int i = 0; i < first_after_; i++)
 | 
						|
		merged_maps_by_address[i] = map__get(kmaps_maps_by_address[i]);
 | 
						|
 | 
						|
	maps__set_nr_maps(kmaps, first_after_);
 | 
						|
 | 
						|
	/* Add the new map, it will be split when the later overlapping mappings are added. */
 | 
						|
	__maps__insert(kmaps, new_map);
 | 
						|
 | 
						|
	/* Insert mappings after new_map, splitting new_map in the process. */
 | 
						|
	for (unsigned int i = first_after_; i < kmaps__nr_maps; i++)
 | 
						|
		__maps__fixup_overlap_and_insert(kmaps, kmaps_maps_by_address[i]);
 | 
						|
 | 
						|
	/* Copy the maps from merged into kmaps. */
 | 
						|
	for (unsigned int i = 0; i < kmaps__nr_maps; i++)
 | 
						|
		map__zput(kmaps_maps_by_address[i]);
 | 
						|
 | 
						|
	free(kmaps_maps_by_address);
 | 
						|
	check_invariants(kmaps);
 | 
						|
	up_write(maps__lock(kmaps));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void maps__load_first(struct maps *maps)
 | 
						|
{
 | 
						|
	down_read(maps__lock(maps));
 | 
						|
 | 
						|
	if (maps__nr_maps(maps) > 0)
 | 
						|
		map__load(maps__maps_by_address(maps)[0]);
 | 
						|
 | 
						|
	up_read(maps__lock(maps));
 | 
						|
}
 |