diff options
Diffstat (limited to 'tools/perf/lib')
| -rw-r--r-- | tools/perf/lib/Build | 1 | ||||
| -rw-r--r-- | tools/perf/lib/Makefile | 6 | ||||
| -rw-r--r-- | tools/perf/lib/core.c | 3 | ||||
| -rw-r--r-- | tools/perf/lib/cpumap.c | 73 | ||||
| -rw-r--r-- | tools/perf/lib/evlist.c | 358 | ||||
| -rw-r--r-- | tools/perf/lib/evsel.c | 75 | ||||
| -rw-r--r-- | tools/perf/lib/include/internal/evlist.h | 44 | ||||
| -rw-r--r-- | tools/perf/lib/include/internal/evsel.h | 1 | ||||
| -rw-r--r-- | tools/perf/lib/include/internal/mmap.h | 45 | ||||
| -rw-r--r-- | tools/perf/lib/include/internal/tests.h | 20 | ||||
| -rw-r--r-- | tools/perf/lib/include/perf/core.h | 3 | ||||
| -rw-r--r-- | tools/perf/lib/include/perf/cpumap.h | 2 | ||||
| -rw-r--r-- | tools/perf/lib/include/perf/evlist.h | 15 | ||||
| -rw-r--r-- | tools/perf/lib/include/perf/evsel.h | 5 | ||||
| -rw-r--r-- | tools/perf/lib/include/perf/mmap.h | 15 | ||||
| -rw-r--r-- | tools/perf/lib/internal.h | 5 | ||||
| -rw-r--r-- | tools/perf/lib/libperf.map | 10 | ||||
| -rw-r--r-- | tools/perf/lib/mmap.c | 275 | ||||
| -rw-r--r-- | tools/perf/lib/tests/Makefile | 6 | ||||
| -rw-r--r-- | tools/perf/lib/tests/test-cpumap.c | 2 | ||||
| -rw-r--r-- | tools/perf/lib/tests/test-evlist.c | 219 | ||||
| -rw-r--r-- | tools/perf/lib/tests/test-evsel.c | 2 | ||||
| -rw-r--r-- | tools/perf/lib/tests/test-threadmap.c | 2 | 
23 files changed, 1139 insertions, 48 deletions
diff --git a/tools/perf/lib/Build b/tools/perf/lib/Build index c31f1c111f8f..2ef9a4ec6d99 100644 --- a/tools/perf/lib/Build +++ b/tools/perf/lib/Build @@ -3,6 +3,7 @@ libperf-y += cpumap.o  libperf-y += threadmap.o  libperf-y += evsel.o  libperf-y += evlist.o +libperf-y += mmap.o  libperf-y += zalloc.o  libperf-y += xyarray.o  libperf-y += lib.o diff --git a/tools/perf/lib/Makefile b/tools/perf/lib/Makefile index 85ccb8c439a4..0f233638ef1f 100644 --- a/tools/perf/lib/Makefile +++ b/tools/perf/lib/Makefile @@ -107,6 +107,7 @@ else  endif  LIBAPI = $(API_PATH)libapi.a +export LIBAPI  $(LIBAPI): FORCE  	$(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) $(OUTPUT)libapi.a @@ -172,8 +173,9 @@ install_headers:  		$(call do_install,include/perf/cpumap.h,$(prefix)/include/perf,644); \  		$(call do_install,include/perf/threadmap.h,$(prefix)/include/perf,644); \  		$(call do_install,include/perf/evlist.h,$(prefix)/include/perf,644); \ -		$(call do_install,include/perf/evsel.h,$(prefix)/include/perf,644); -		$(call do_install,include/perf/event.h,$(prefix)/include/perf,644); +		$(call do_install,include/perf/evsel.h,$(prefix)/include/perf,644); \ +		$(call do_install,include/perf/event.h,$(prefix)/include/perf,644); \ +		$(call do_install,include/perf/mmap.h,$(prefix)/include/perf,644);  install_pkgconfig: $(LIBPERF_PC)  	$(call QUIET_INSTALL, $(LIBPERF_PC)) \ diff --git a/tools/perf/lib/core.c b/tools/perf/lib/core.c index d0b9ae422b9f..58fc894b76c5 100644 --- a/tools/perf/lib/core.c +++ b/tools/perf/lib/core.c @@ -5,11 +5,12 @@  #include <stdio.h>  #include <stdarg.h>  #include <unistd.h> +#include <linux/compiler.h>  #include <perf/core.h>  #include <internal/lib.h>  #include "internal.h" -static int __base_pr(enum libperf_print_level level, const char *format, +static int __base_pr(enum libperf_print_level level __maybe_unused, const char *format,  		     va_list args)  {  	return vfprintf(stderr, format, args); diff --git a/tools/perf/lib/cpumap.c b/tools/perf/lib/cpumap.c index 2ca1fafa620d..f93f4e703e4c 100644 --- a/tools/perf/lib/cpumap.c +++ b/tools/perf/lib/cpumap.c @@ -68,14 +68,28 @@ static struct perf_cpu_map *cpu_map__default_new(void)  	return cpus;  } +static int cmp_int(const void *a, const void *b) +{ +	return *(const int *)a - *(const int*)b; +} +  static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)  {  	size_t payload_size = nr_cpus * sizeof(int);  	struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + payload_size); +	int i, j;  	if (cpus != NULL) { -		cpus->nr = nr_cpus;  		memcpy(cpus->map, tmp_cpus, payload_size); +		qsort(cpus->map, nr_cpus, sizeof(int), cmp_int); +		/* Remove dups */ +		j = 0; +		for (i = 0; i < nr_cpus; i++) { +			if (i == 0 || cpus->map[i] != cpus->map[i - 1]) +				cpus->map[j++] = cpus->map[i]; +		} +		cpus->nr = j; +		assert(j <= nr_cpus);  		refcount_set(&cpus->refcnt, 1);  	} @@ -272,3 +286,60 @@ int perf_cpu_map__max(struct perf_cpu_map *map)  	return max;  } + +/* + * Merge two cpumaps + * + * orig either gets freed and replaced with a new map, or reused + * with no reference count change (similar to "realloc") + * other has its reference count increased. + */ + +struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, +					 struct perf_cpu_map *other) +{ +	int *tmp_cpus; +	int tmp_len; +	int i, j, k; +	struct perf_cpu_map *merged; + +	if (!orig && !other) +		return NULL; +	if (!orig) { +		perf_cpu_map__get(other); +		return other; +	} +	if (!other) +		return orig; +	if (orig->nr == other->nr && +	    !memcmp(orig->map, other->map, orig->nr * sizeof(int))) +		return orig; + +	tmp_len = orig->nr + other->nr; +	tmp_cpus = malloc(tmp_len * sizeof(int)); +	if (!tmp_cpus) +		return NULL; + +	/* Standard merge algorithm from wikipedia */ +	i = j = k = 0; +	while (i < orig->nr && j < other->nr) { +		if (orig->map[i] <= other->map[j]) { +			if (orig->map[i] == other->map[j]) +				j++; +			tmp_cpus[k++] = orig->map[i++]; +		} else +			tmp_cpus[k++] = other->map[j++]; +	} + +	while (i < orig->nr) +		tmp_cpus[k++] = orig->map[i++]; + +	while (j < other->nr) +		tmp_cpus[k++] = other->map[j++]; +	assert(k <= tmp_len); + +	merged = cpu_map__trim_new(k, tmp_cpus); +	free(tmp_cpus); +	perf_cpu_map__put(orig); +	return merged; +} diff --git a/tools/perf/lib/evlist.c b/tools/perf/lib/evlist.c index d1496fee810c..ae9e65aa2491 100644 --- a/tools/perf/lib/evlist.c +++ b/tools/perf/lib/evlist.c @@ -8,13 +8,20 @@  #include <internal/evlist.h>  #include <internal/evsel.h>  #include <internal/xyarray.h> +#include <internal/mmap.h> +#include <internal/cpumap.h> +#include <internal/threadmap.h> +#include <internal/xyarray.h> +#include <internal/lib.h>  #include <linux/zalloc.h> +#include <sys/ioctl.h>  #include <stdlib.h>  #include <errno.h>  #include <unistd.h>  #include <fcntl.h>  #include <signal.h>  #include <poll.h> +#include <sys/mman.h>  #include <perf/cpumap.h>  #include <perf/threadmap.h>  #include <api/fd/array.h> @@ -27,6 +34,7 @@ void perf_evlist__init(struct perf_evlist *evlist)  		INIT_HLIST_HEAD(&evlist->heads[i]);  	INIT_LIST_HEAD(&evlist->entries);  	evlist->nr_entries = 0; +	fdarray__init(&evlist->pollfd, 64);  }  static void __perf_evlist__propagate_maps(struct perf_evlist *evlist, @@ -46,6 +54,7 @@ static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,  	perf_thread_map__put(evsel->threads);  	evsel->threads = perf_thread_map__get(evlist->threads); +	evlist->all_cpus = perf_cpu_map__merge(evlist->all_cpus, evsel->cpus);  }  static void perf_evlist__propagate_maps(struct perf_evlist *evlist) @@ -101,8 +110,36 @@ perf_evlist__next(struct perf_evlist *evlist, struct perf_evsel *prev)  	return next;  } +static void perf_evlist__purge(struct perf_evlist *evlist) +{ +	struct perf_evsel *pos, *n; + +	perf_evlist__for_each_entry_safe(evlist, n, pos) { +		list_del_init(&pos->node); +		perf_evsel__delete(pos); +	} + +	evlist->nr_entries = 0; +} + +void perf_evlist__exit(struct perf_evlist *evlist) +{ +	perf_cpu_map__put(evlist->cpus); +	perf_thread_map__put(evlist->threads); +	evlist->cpus = NULL; +	evlist->threads = NULL; +	fdarray__exit(&evlist->pollfd); +} +  void perf_evlist__delete(struct perf_evlist *evlist)  { +	if (evlist == NULL) +		return; + +	perf_evlist__munmap(evlist); +	perf_evlist__close(evlist); +	perf_evlist__purge(evlist); +	perf_evlist__exit(evlist);  	free(evlist);  } @@ -277,7 +314,328 @@ int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,  	return pos;  } +static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd, +					 void *arg __maybe_unused) +{ +	struct perf_mmap *map = fda->priv[fd].ptr; + +	if (map) +		perf_mmap__put(map); +} + +int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask) +{ +	return fdarray__filter(&evlist->pollfd, revents_and_mask, +			       perf_evlist__munmap_filtered, NULL); +} +  int perf_evlist__poll(struct perf_evlist *evlist, int timeout)  {  	return fdarray__poll(&evlist->pollfd, timeout);  } + +static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, bool overwrite) +{ +	int i; +	struct perf_mmap *map; + +	map = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); +	if (!map) +		return NULL; + +	for (i = 0; i < evlist->nr_mmaps; i++) { +		struct perf_mmap *prev = i ? &map[i - 1] : NULL; + +		/* +		 * When the perf_mmap() call is made we grab one refcount, plus +		 * one extra to let perf_mmap__consume() get the last +		 * events after all real references (perf_mmap__get()) are +		 * dropped. +		 * +		 * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and +		 * thus does perf_mmap__get() on it. +		 */ +		perf_mmap__init(&map[i], prev, overwrite, NULL); +	} + +	return map; +} + +static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, +				     struct perf_evsel *evsel, int idx, int cpu, +				     int thread) +{ +	struct perf_sample_id *sid = SID(evsel, cpu, thread); + +	sid->idx = idx; +	if (evlist->cpus && cpu >= 0) +		sid->cpu = evlist->cpus->map[cpu]; +	else +		sid->cpu = -1; +	if (!evsel->system_wide && evlist->threads && thread >= 0) +		sid->tid = perf_thread_map__pid(evlist->threads, thread); +	else +		sid->tid = -1; +} + +static struct perf_mmap* +perf_evlist__mmap_cb_get(struct perf_evlist *evlist, bool overwrite, int idx) +{ +	struct perf_mmap *maps; + +	maps = overwrite ? evlist->mmap_ovw : evlist->mmap; + +	if (!maps) { +		maps = perf_evlist__alloc_mmap(evlist, overwrite); +		if (!maps) +			return NULL; + +		if (overwrite) +			evlist->mmap_ovw = maps; +		else +			evlist->mmap = maps; +	} + +	return &maps[idx]; +} + +#define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y)) + +static int +perf_evlist__mmap_cb_mmap(struct perf_mmap *map, struct perf_mmap_param *mp, +			  int output, int cpu) +{ +	return perf_mmap__mmap(map, mp, output, cpu); +} + +static void perf_evlist__set_mmap_first(struct perf_evlist *evlist, struct perf_mmap *map, +					bool overwrite) +{ +	if (overwrite) +		evlist->mmap_ovw_first = map; +	else +		evlist->mmap_first = map; +} + +static int +mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, +	       int idx, struct perf_mmap_param *mp, int cpu_idx, +	       int thread, int *_output, int *_output_overwrite) +{ +	int evlist_cpu = perf_cpu_map__cpu(evlist->cpus, cpu_idx); +	struct perf_evsel *evsel; +	int revent; + +	perf_evlist__for_each_entry(evlist, evsel) { +		bool overwrite = evsel->attr.write_backward; +		struct perf_mmap *map; +		int *output, fd, cpu; + +		if (evsel->system_wide && thread) +			continue; + +		cpu = perf_cpu_map__idx(evsel->cpus, evlist_cpu); +		if (cpu == -1) +			continue; + +		map = ops->get(evlist, overwrite, idx); +		if (map == NULL) +			return -ENOMEM; + +		if (overwrite) { +			mp->prot = PROT_READ; +			output   = _output_overwrite; +		} else { +			mp->prot = PROT_READ | PROT_WRITE; +			output   = _output; +		} + +		fd = FD(evsel, cpu, thread); + +		if (*output == -1) { +			*output = fd; + +			/* +			 * The last one will be done at perf_mmap__consume(), so that we +			 * make sure we don't prevent tools from consuming every last event in +			 * the ring buffer. +			 * +			 * I.e. we can get the POLLHUP meaning that the fd doesn't exist +			 * anymore, but the last events for it are still in the ring buffer, +			 * waiting to be consumed. +			 * +			 * Tools can chose to ignore this at their own discretion, but the +			 * evlist layer can't just drop it when filtering events in +			 * perf_evlist__filter_pollfd(). +			 */ +			refcount_set(&map->refcnt, 2); + +			if (ops->mmap(map, mp, *output, evlist_cpu) < 0) +				return -1; + +			if (!idx) +				perf_evlist__set_mmap_first(evlist, map, overwrite); +		} else { +			if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) +				return -1; + +			perf_mmap__get(map); +		} + +		revent = !overwrite ? POLLIN : 0; + +		if (!evsel->system_wide && +		    perf_evlist__add_pollfd(evlist, fd, map, revent) < 0) { +			perf_mmap__put(map); +			return -1; +		} + +		if (evsel->attr.read_format & PERF_FORMAT_ID) { +			if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread, +						   fd) < 0) +				return -1; +			perf_evlist__set_sid_idx(evlist, evsel, idx, cpu, +						 thread); +		} +	} + +	return 0; +} + +static int +mmap_per_thread(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, +		struct perf_mmap_param *mp) +{ +	int thread; +	int nr_threads = perf_thread_map__nr(evlist->threads); + +	for (thread = 0; thread < nr_threads; thread++) { +		int output = -1; +		int output_overwrite = -1; + +		if (ops->idx) +			ops->idx(evlist, mp, thread, false); + +		if (mmap_per_evsel(evlist, ops, thread, mp, 0, thread, +				   &output, &output_overwrite)) +			goto out_unmap; +	} + +	return 0; + +out_unmap: +	perf_evlist__munmap(evlist); +	return -1; +} + +static int +mmap_per_cpu(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, +	     struct perf_mmap_param *mp) +{ +	int nr_threads = perf_thread_map__nr(evlist->threads); +	int nr_cpus    = perf_cpu_map__nr(evlist->cpus); +	int cpu, thread; + +	for (cpu = 0; cpu < nr_cpus; cpu++) { +		int output = -1; +		int output_overwrite = -1; + +		if (ops->idx) +			ops->idx(evlist, mp, cpu, true); + +		for (thread = 0; thread < nr_threads; thread++) { +			if (mmap_per_evsel(evlist, ops, cpu, mp, cpu, +					   thread, &output, &output_overwrite)) +				goto out_unmap; +		} +	} + +	return 0; + +out_unmap: +	perf_evlist__munmap(evlist); +	return -1; +} + +static int perf_evlist__nr_mmaps(struct perf_evlist *evlist) +{ +	int nr_mmaps; + +	nr_mmaps = perf_cpu_map__nr(evlist->cpus); +	if (perf_cpu_map__empty(evlist->cpus)) +		nr_mmaps = perf_thread_map__nr(evlist->threads); + +	return nr_mmaps; +} + +int perf_evlist__mmap_ops(struct perf_evlist *evlist, +			  struct perf_evlist_mmap_ops *ops, +			  struct perf_mmap_param *mp) +{ +	struct perf_evsel *evsel; +	const struct perf_cpu_map *cpus = evlist->cpus; +	const struct perf_thread_map *threads = evlist->threads; + +	if (!ops || !ops->get || !ops->mmap) +		return -EINVAL; + +	mp->mask = evlist->mmap_len - page_size - 1; + +	evlist->nr_mmaps = perf_evlist__nr_mmaps(evlist); + +	perf_evlist__for_each_entry(evlist, evsel) { +		if ((evsel->attr.read_format & PERF_FORMAT_ID) && +		    evsel->sample_id == NULL && +		    perf_evsel__alloc_id(evsel, perf_cpu_map__nr(cpus), threads->nr) < 0) +			return -ENOMEM; +	} + +	if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0) +		return -ENOMEM; + +	if (perf_cpu_map__empty(cpus)) +		return mmap_per_thread(evlist, ops, mp); + +	return mmap_per_cpu(evlist, ops, mp); +} + +int perf_evlist__mmap(struct perf_evlist *evlist, int pages) +{ +	struct perf_mmap_param mp; +	struct perf_evlist_mmap_ops ops = { +		.get  = perf_evlist__mmap_cb_get, +		.mmap = perf_evlist__mmap_cb_mmap, +	}; + +	evlist->mmap_len = (pages + 1) * page_size; + +	return perf_evlist__mmap_ops(evlist, &ops, &mp); +} + +void perf_evlist__munmap(struct perf_evlist *evlist) +{ +	int i; + +	if (evlist->mmap) { +		for (i = 0; i < evlist->nr_mmaps; i++) +			perf_mmap__munmap(&evlist->mmap[i]); +	} + +	if (evlist->mmap_ovw) { +		for (i = 0; i < evlist->nr_mmaps; i++) +			perf_mmap__munmap(&evlist->mmap_ovw[i]); +	} + +	zfree(&evlist->mmap); +	zfree(&evlist->mmap_ovw); +} + +struct perf_mmap* +perf_evlist__next_mmap(struct perf_evlist *evlist, struct perf_mmap *map, +		       bool overwrite) +{ +	if (map) +		return map->next; + +	return overwrite ? evlist->mmap_ovw_first : evlist->mmap_first; +} diff --git a/tools/perf/lib/evsel.c b/tools/perf/lib/evsel.c index a8cb582e2721..4dc06289f4c7 100644 --- a/tools/perf/lib/evsel.c +++ b/tools/perf/lib/evsel.c @@ -114,15 +114,23 @@ int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus,  	return err;  } +static void perf_evsel__close_fd_cpu(struct perf_evsel *evsel, int cpu) +{ +	int thread; + +	for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) { +		if (FD(evsel, cpu, thread) >= 0) +			close(FD(evsel, cpu, thread)); +		FD(evsel, cpu, thread) = -1; +	} +} +  void perf_evsel__close_fd(struct perf_evsel *evsel)  { -	int cpu, thread; +	int cpu;  	for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) -		for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) { -			close(FD(evsel, cpu, thread)); -			FD(evsel, cpu, thread) = -1; -		} +		perf_evsel__close_fd_cpu(evsel, cpu);  }  void perf_evsel__free_fd(struct perf_evsel *evsel) @@ -140,6 +148,14 @@ void perf_evsel__close(struct perf_evsel *evsel)  	perf_evsel__free_fd(evsel);  } +void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu) +{ +	if (evsel->fd == NULL) +		return; + +	perf_evsel__close_fd_cpu(evsel, cpu); +} +  int perf_evsel__read_size(struct perf_evsel *evsel)  {  	u64 read_format = evsel->attr.read_format; @@ -182,38 +198,61 @@ int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,  }  static int perf_evsel__run_ioctl(struct perf_evsel *evsel, -				 int ioc,  void *arg) +				 int ioc,  void *arg, +				 int cpu)  { -	int cpu, thread; +	int thread; -	for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) { -		for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { -			int fd = FD(evsel, cpu, thread), -			    err = ioctl(fd, ioc, arg); +	for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { +		int fd = FD(evsel, cpu, thread), +		    err = ioctl(fd, ioc, arg); -			if (err) -				return err; -		} +		if (err) +			return err;  	}  	return 0;  } +int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu) +{ +	return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, cpu); +} +  int perf_evsel__enable(struct perf_evsel *evsel)  { -	return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, 0); +	int i; +	int err = 0; + +	for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) +		err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, i); +	return err; +} + +int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu) +{ +	return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, cpu);  }  int perf_evsel__disable(struct perf_evsel *evsel)  { -	return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, 0); +	int i; +	int err = 0; + +	for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) +		err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, i); +	return err;  }  int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter)  { -	return perf_evsel__run_ioctl(evsel, +	int err = 0, i; + +	for (i = 0; i < evsel->cpus->nr && !err; i++) +		err = perf_evsel__run_ioctl(evsel,  				     PERF_EVENT_IOC_SET_FILTER, -				     (void *)filter); +				     (void *)filter, i); +	return err;  }  struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) diff --git a/tools/perf/lib/include/internal/evlist.h b/tools/perf/lib/include/internal/evlist.h index 9f440ab12b76..74dc8c3f0b66 100644 --- a/tools/perf/lib/include/internal/evlist.h +++ b/tools/perf/lib/include/internal/evlist.h @@ -11,23 +11,49 @@  struct perf_cpu_map;  struct perf_thread_map; +struct perf_mmap_param;  struct perf_evlist {  	struct list_head	 entries;  	int			 nr_entries;  	bool			 has_user_cpus;  	struct perf_cpu_map	*cpus; +	struct perf_cpu_map	*all_cpus;  	struct perf_thread_map	*threads;  	int			 nr_mmaps;  	size_t			 mmap_len;  	struct fdarray		 pollfd;  	struct hlist_head	 heads[PERF_EVLIST__HLIST_SIZE]; +	struct perf_mmap	*mmap; +	struct perf_mmap	*mmap_ovw; +	struct perf_mmap	*mmap_first; +	struct perf_mmap	*mmap_ovw_first; +}; + +typedef void +(*perf_evlist_mmap__cb_idx_t)(struct perf_evlist*, struct perf_mmap_param*, int, bool); +typedef struct perf_mmap* +(*perf_evlist_mmap__cb_get_t)(struct perf_evlist*, bool, int); +typedef int +(*perf_evlist_mmap__cb_mmap_t)(struct perf_mmap*, struct perf_mmap_param*, int, int); + +struct perf_evlist_mmap_ops { +	perf_evlist_mmap__cb_idx_t	idx; +	perf_evlist_mmap__cb_get_t	get; +	perf_evlist_mmap__cb_mmap_t	mmap;  };  int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);  int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,  			    void *ptr, short revent); +int perf_evlist__mmap_ops(struct perf_evlist *evlist, +			  struct perf_evlist_mmap_ops *ops, +			  struct perf_mmap_param *mp); + +void perf_evlist__init(struct perf_evlist *evlist); +void perf_evlist__exit(struct perf_evlist *evlist); +  /**   * __perf_evlist__for_each_entry - iterate thru all the evsels   * @list: list_head instance to iterate @@ -60,6 +86,24 @@ int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,  #define perf_evlist__for_each_entry_reverse(evlist, evsel) \  	__perf_evlist__for_each_entry_reverse(&(evlist)->entries, evsel) +/** + * __perf_evlist__for_each_entry_safe - safely iterate thru all the evsels + * @list: list_head instance to iterate + * @tmp: struct evsel temp iterator + * @evsel: struct evsel iterator + */ +#define __perf_evlist__for_each_entry_safe(list, tmp, evsel) \ +	list_for_each_entry_safe(evsel, tmp, list, node) + +/** + * perf_evlist__for_each_entry_safe - safely iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + * @tmp: struct evsel temp iterator + */ +#define perf_evlist__for_each_entry_safe(evlist, tmp, evsel) \ +	__perf_evlist__for_each_entry_safe(&(evlist)->entries, tmp, evsel) +  static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist)  {  	return list_entry(evlist->entries.next, struct perf_evsel, node); diff --git a/tools/perf/lib/include/internal/evsel.h b/tools/perf/lib/include/internal/evsel.h index a69b8299c36f..1ffd083b235e 100644 --- a/tools/perf/lib/include/internal/evsel.h +++ b/tools/perf/lib/include/internal/evsel.h @@ -50,6 +50,7 @@ struct perf_evsel {  	bool			 system_wide;  }; +void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr);  int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);  void perf_evsel__close_fd(struct perf_evsel *evsel);  void perf_evsel__free_fd(struct perf_evsel *evsel); diff --git a/tools/perf/lib/include/internal/mmap.h b/tools/perf/lib/include/internal/mmap.h index ba1e519c15b9..be7556e0a2b2 100644 --- a/tools/perf/lib/include/internal/mmap.h +++ b/tools/perf/lib/include/internal/mmap.h @@ -10,23 +10,46 @@  /* perf sample has 16 bits size limit */  #define PERF_SAMPLE_MAX_SIZE (1 << 16) +struct perf_mmap; + +typedef void (*libperf_unmap_cb_t)(struct perf_mmap *map); +  /**   * struct perf_mmap - perf's ring buffer mmap details   *   * @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this   */  struct perf_mmap { -	void		*base; -	int		 mask; -	int		 fd; -	int		 cpu; -	refcount_t	 refcnt; -	u64		 prev; -	u64		 start; -	u64		 end; -	bool		 overwrite; -	u64		 flush; -	char		 event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8); +	void			*base; +	int			 mask; +	int			 fd; +	int			 cpu; +	refcount_t		 refcnt; +	u64			 prev; +	u64			 start; +	u64			 end; +	bool			 overwrite; +	u64			 flush; +	libperf_unmap_cb_t	 unmap_cb; +	char			 event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8); +	struct perf_mmap	*next; +}; + +struct perf_mmap_param { +	int	prot; +	int	mask;  }; +size_t perf_mmap__mmap_len(struct perf_mmap *map); + +void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, +		     bool overwrite, libperf_unmap_cb_t unmap_cb); +int perf_mmap__mmap(struct perf_mmap *map, struct perf_mmap_param *mp, +		    int fd, int cpu); +void perf_mmap__munmap(struct perf_mmap *map); +void perf_mmap__get(struct perf_mmap *map); +void perf_mmap__put(struct perf_mmap *map); + +u64 perf_mmap__read_head(struct perf_mmap *map); +  #endif /* __LIBPERF_INTERNAL_MMAP_H */ diff --git a/tools/perf/lib/include/internal/tests.h b/tools/perf/lib/include/internal/tests.h index b7a20cd24ee1..2093e8868a67 100644 --- a/tools/perf/lib/include/internal/tests.h +++ b/tools/perf/lib/include/internal/tests.h @@ -4,14 +4,28 @@  #include <stdio.h> -#define __T_START fprintf(stdout, "- running %s...", __FILE__) -#define __T_OK    fprintf(stdout, "OK\n") -#define __T_FAIL  fprintf(stdout, "FAIL\n") +int tests_failed; + +#define __T_START					\ +do {							\ +	fprintf(stdout, "- running %s...", __FILE__);	\ +	fflush(NULL);					\ +	tests_failed = 0;				\ +} while (0) + +#define __T_END								\ +do {									\ +	if (tests_failed)						\ +		fprintf(stdout, "  FAILED (%d)\n", tests_failed);	\ +	else								\ +		fprintf(stdout, "OK\n");				\ +} while (0)  #define __T(text, cond)                                                          \  do {                                                                             \  	if (!(cond)) {                                                           \  		fprintf(stderr, "FAILED %s:%d %s\n", __FILE__, __LINE__, text);  \ +		tests_failed++;                                                  \  		return -1;                                                       \  	}                                                                        \  } while (0) diff --git a/tools/perf/lib/include/perf/core.h b/tools/perf/lib/include/perf/core.h index cfd70e720c1c..a3f6d68edad7 100644 --- a/tools/perf/lib/include/perf/core.h +++ b/tools/perf/lib/include/perf/core.h @@ -9,9 +9,12 @@  #endif  enum libperf_print_level { +	LIBPERF_ERR,  	LIBPERF_WARN,  	LIBPERF_INFO,  	LIBPERF_DEBUG, +	LIBPERF_DEBUG2, +	LIBPERF_DEBUG3,  };  typedef int (*libperf_print_fn_t)(enum libperf_print_level level, diff --git a/tools/perf/lib/include/perf/cpumap.h b/tools/perf/lib/include/perf/cpumap.h index ac9aa497f84a..6a17ad730cbc 100644 --- a/tools/perf/lib/include/perf/cpumap.h +++ b/tools/perf/lib/include/perf/cpumap.h @@ -12,6 +12,8 @@ LIBPERF_API struct perf_cpu_map *perf_cpu_map__dummy_new(void);  LIBPERF_API struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list);  LIBPERF_API struct perf_cpu_map *perf_cpu_map__read(FILE *file);  LIBPERF_API struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map); +LIBPERF_API struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, +						     struct perf_cpu_map *other);  LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map);  LIBPERF_API int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx);  LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus); diff --git a/tools/perf/lib/include/perf/evlist.h b/tools/perf/lib/include/perf/evlist.h index 8a2ce0757ab2..0a7479dc13bf 100644 --- a/tools/perf/lib/include/perf/evlist.h +++ b/tools/perf/lib/include/perf/evlist.h @@ -3,13 +3,13 @@  #define __LIBPERF_EVLIST_H  #include <perf/core.h> +#include <stdbool.h>  struct perf_evlist;  struct perf_evsel;  struct perf_cpu_map;  struct perf_thread_map; -LIBPERF_API void perf_evlist__init(struct perf_evlist *evlist);  LIBPERF_API void perf_evlist__add(struct perf_evlist *evlist,  				  struct perf_evsel *evsel);  LIBPERF_API void perf_evlist__remove(struct perf_evlist *evlist, @@ -32,5 +32,18 @@ LIBPERF_API void perf_evlist__set_maps(struct perf_evlist *evlist,  				       struct perf_cpu_map *cpus,  				       struct perf_thread_map *threads);  LIBPERF_API int perf_evlist__poll(struct perf_evlist *evlist, int timeout); +LIBPERF_API int perf_evlist__filter_pollfd(struct perf_evlist *evlist, +					   short revents_and_mask); + +LIBPERF_API int perf_evlist__mmap(struct perf_evlist *evlist, int pages); +LIBPERF_API void perf_evlist__munmap(struct perf_evlist *evlist); + +LIBPERF_API struct perf_mmap *perf_evlist__next_mmap(struct perf_evlist *evlist, +						     struct perf_mmap *map, +						     bool overwrite); +#define perf_evlist__for_each_mmap(evlist, pos, overwrite)		\ +	for ((pos) = perf_evlist__next_mmap((evlist), NULL, overwrite);	\ +	     (pos) != NULL;						\ +	     (pos) = perf_evlist__next_mmap((evlist), (pos), overwrite))  #endif /* __LIBPERF_EVLIST_H */ diff --git a/tools/perf/lib/include/perf/evsel.h b/tools/perf/lib/include/perf/evsel.h index 4388667f265c..c82ec39a4ad0 100644 --- a/tools/perf/lib/include/perf/evsel.h +++ b/tools/perf/lib/include/perf/evsel.h @@ -21,17 +21,18 @@ struct perf_counts_values {  	};  }; -LIBPERF_API void perf_evsel__init(struct perf_evsel *evsel, -				  struct perf_event_attr *attr);  LIBPERF_API struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr);  LIBPERF_API void perf_evsel__delete(struct perf_evsel *evsel);  LIBPERF_API int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus,  				 struct perf_thread_map *threads);  LIBPERF_API void perf_evsel__close(struct perf_evsel *evsel); +LIBPERF_API void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu);  LIBPERF_API int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,  				 struct perf_counts_values *count);  LIBPERF_API int perf_evsel__enable(struct perf_evsel *evsel); +LIBPERF_API int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu);  LIBPERF_API int perf_evsel__disable(struct perf_evsel *evsel); +LIBPERF_API int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu);  LIBPERF_API struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel);  LIBPERF_API struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel);  LIBPERF_API struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel); diff --git a/tools/perf/lib/include/perf/mmap.h b/tools/perf/lib/include/perf/mmap.h new file mode 100644 index 000000000000..9508ad90d8b9 --- /dev/null +++ b/tools/perf/lib/include/perf/mmap.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_MMAP_H +#define __LIBPERF_MMAP_H + +#include <perf/core.h> + +struct perf_mmap; +union perf_event; + +LIBPERF_API void perf_mmap__consume(struct perf_mmap *map); +LIBPERF_API int perf_mmap__read_init(struct perf_mmap *map); +LIBPERF_API void perf_mmap__read_done(struct perf_mmap *map); +LIBPERF_API union perf_event *perf_mmap__read_event(struct perf_mmap *map); + +#endif /* __LIBPERF_MMAP_H */ diff --git a/tools/perf/lib/internal.h b/tools/perf/lib/internal.h index dc92f241732e..2c27e158de6b 100644 --- a/tools/perf/lib/internal.h +++ b/tools/perf/lib/internal.h @@ -2,6 +2,8 @@  #ifndef __LIBPERF_INTERNAL_H  #define __LIBPERF_INTERNAL_H +#include <perf/core.h> +  void libperf_print(enum libperf_print_level level,  		   const char *format, ...)  	__attribute__((format(printf, 2, 3))); @@ -11,8 +13,11 @@ do {                            \  	libperf_print(level, "libperf: " fmt, ##__VA_ARGS__);     \  } while (0) +#define pr_err(fmt, ...)        __pr(LIBPERF_ERR, fmt, ##__VA_ARGS__)  #define pr_warning(fmt, ...)    __pr(LIBPERF_WARN, fmt, ##__VA_ARGS__)  #define pr_info(fmt, ...)       __pr(LIBPERF_INFO, fmt, ##__VA_ARGS__)  #define pr_debug(fmt, ...)      __pr(LIBPERF_DEBUG, fmt, ##__VA_ARGS__) +#define pr_debug2(fmt, ...)     __pr(LIBPERF_DEBUG2, fmt, ##__VA_ARGS__) +#define pr_debug3(fmt, ...)     __pr(LIBPERF_DEBUG3, fmt, ##__VA_ARGS__)  #endif /* __LIBPERF_INTERNAL_H */ diff --git a/tools/perf/lib/libperf.map b/tools/perf/lib/libperf.map index ab8dbde1136c..7be1af8a546c 100644 --- a/tools/perf/lib/libperf.map +++ b/tools/perf/lib/libperf.map @@ -21,7 +21,6 @@ LIBPERF_0.0.1 {  		perf_evsel__delete;  		perf_evsel__enable;  		perf_evsel__disable; -		perf_evsel__init;  		perf_evsel__open;  		perf_evsel__close;  		perf_evsel__read; @@ -34,12 +33,19 @@ LIBPERF_0.0.1 {  		perf_evlist__close;  		perf_evlist__enable;  		perf_evlist__disable; -		perf_evlist__init;  		perf_evlist__add;  		perf_evlist__remove;  		perf_evlist__next;  		perf_evlist__set_maps;  		perf_evlist__poll; +		perf_evlist__mmap; +		perf_evlist__munmap; +		perf_evlist__filter_pollfd; +		perf_evlist__next_mmap; +		perf_mmap__consume; +		perf_mmap__read_init; +		perf_mmap__read_done; +		perf_mmap__read_event;  	local:  		*;  }; diff --git a/tools/perf/lib/mmap.c b/tools/perf/lib/mmap.c new file mode 100644 index 000000000000..79d5ed6c38cc --- /dev/null +++ b/tools/perf/lib/mmap.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/mman.h> +#include <inttypes.h> +#include <asm/bug.h> +#include <errno.h> +#include <string.h> +#include <linux/ring_buffer.h> +#include <linux/perf_event.h> +#include <perf/mmap.h> +#include <perf/event.h> +#include <internal/mmap.h> +#include <internal/lib.h> +#include <linux/kernel.h> +#include "internal.h" + +void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, +		     bool overwrite, libperf_unmap_cb_t unmap_cb) +{ +	map->fd = -1; +	map->overwrite = overwrite; +	map->unmap_cb  = unmap_cb; +	refcount_set(&map->refcnt, 0); +	if (prev) +		prev->next = map; +} + +size_t perf_mmap__mmap_len(struct perf_mmap *map) +{ +	return map->mask + 1 + page_size; +} + +int perf_mmap__mmap(struct perf_mmap *map, struct perf_mmap_param *mp, +		    int fd, int cpu) +{ +	map->prev = 0; +	map->mask = mp->mask; +	map->base = mmap(NULL, perf_mmap__mmap_len(map), mp->prot, +			 MAP_SHARED, fd, 0); +	if (map->base == MAP_FAILED) { +		map->base = NULL; +		return -1; +	} + +	map->fd  = fd; +	map->cpu = cpu; +	return 0; +} + +void perf_mmap__munmap(struct perf_mmap *map) +{ +	if (map && map->base != NULL) { +		munmap(map->base, perf_mmap__mmap_len(map)); +		map->base = NULL; +		map->fd = -1; +		refcount_set(&map->refcnt, 0); +	} +	if (map && map->unmap_cb) +		map->unmap_cb(map); +} + +void perf_mmap__get(struct perf_mmap *map) +{ +	refcount_inc(&map->refcnt); +} + +void perf_mmap__put(struct perf_mmap *map) +{ +	BUG_ON(map->base && refcount_read(&map->refcnt) == 0); + +	if (refcount_dec_and_test(&map->refcnt)) +		perf_mmap__munmap(map); +} + +static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail) +{ +	ring_buffer_write_tail(md->base, tail); +} + +u64 perf_mmap__read_head(struct perf_mmap *map) +{ +	return ring_buffer_read_head(map->base); +} + +static bool perf_mmap__empty(struct perf_mmap *map) +{ +	struct perf_event_mmap_page *pc = map->base; + +	return perf_mmap__read_head(map) == map->prev && !pc->aux_size; +} + +void perf_mmap__consume(struct perf_mmap *map) +{ +	if (!map->overwrite) { +		u64 old = map->prev; + +		perf_mmap__write_tail(map, old); +	} + +	if (refcount_read(&map->refcnt) == 1 && perf_mmap__empty(map)) +		perf_mmap__put(map); +} + +static int overwrite_rb_find_range(void *buf, int mask, u64 *start, u64 *end) +{ +	struct perf_event_header *pheader; +	u64 evt_head = *start; +	int size = mask + 1; + +	pr_debug2("%s: buf=%p, start=%"PRIx64"\n", __func__, buf, *start); +	pheader = (struct perf_event_header *)(buf + (*start & mask)); +	while (true) { +		if (evt_head - *start >= (unsigned int)size) { +			pr_debug("Finished reading overwrite ring buffer: rewind\n"); +			if (evt_head - *start > (unsigned int)size) +				evt_head -= pheader->size; +			*end = evt_head; +			return 0; +		} + +		pheader = (struct perf_event_header *)(buf + (evt_head & mask)); + +		if (pheader->size == 0) { +			pr_debug("Finished reading overwrite ring buffer: get start\n"); +			*end = evt_head; +			return 0; +		} + +		evt_head += pheader->size; +		pr_debug3("move evt_head: %"PRIx64"\n", evt_head); +	} +	WARN_ONCE(1, "Shouldn't get here\n"); +	return -1; +} + +/* + * Report the start and end of the available data in ringbuffer + */ +static int __perf_mmap__read_init(struct perf_mmap *md) +{ +	u64 head = perf_mmap__read_head(md); +	u64 old = md->prev; +	unsigned char *data = md->base + page_size; +	unsigned long size; + +	md->start = md->overwrite ? head : old; +	md->end = md->overwrite ? old : head; + +	if ((md->end - md->start) < md->flush) +		return -EAGAIN; + +	size = md->end - md->start; +	if (size > (unsigned long)(md->mask) + 1) { +		if (!md->overwrite) { +			WARN_ONCE(1, "failed to keep up with mmap data. (warn only once)\n"); + +			md->prev = head; +			perf_mmap__consume(md); +			return -EAGAIN; +		} + +		/* +		 * Backward ring buffer is full. We still have a chance to read +		 * most of data from it. +		 */ +		if (overwrite_rb_find_range(data, md->mask, &md->start, &md->end)) +			return -EINVAL; +	} + +	return 0; +} + +int perf_mmap__read_init(struct perf_mmap *map) +{ +	/* +	 * Check if event was unmapped due to a POLLHUP/POLLERR. +	 */ +	if (!refcount_read(&map->refcnt)) +		return -ENOENT; + +	return __perf_mmap__read_init(map); +} + +/* + * Mandatory for overwrite mode + * The direction of overwrite mode is backward. + * The last perf_mmap__read() will set tail to map->core.prev. + * Need to correct the map->core.prev to head which is the end of next read. + */ +void perf_mmap__read_done(struct perf_mmap *map) +{ +	/* +	 * Check if event was unmapped due to a POLLHUP/POLLERR. +	 */ +	if (!refcount_read(&map->refcnt)) +		return; + +	map->prev = perf_mmap__read_head(map); +} + +/* When check_messup is true, 'end' must points to a good entry */ +static union perf_event *perf_mmap__read(struct perf_mmap *map, +					 u64 *startp, u64 end) +{ +	unsigned char *data = map->base + page_size; +	union perf_event *event = NULL; +	int diff = end - *startp; + +	if (diff >= (int)sizeof(event->header)) { +		size_t size; + +		event = (union perf_event *)&data[*startp & map->mask]; +		size = event->header.size; + +		if (size < sizeof(event->header) || diff < (int)size) +			return NULL; + +		/* +		 * Event straddles the mmap boundary -- header should always +		 * be inside due to u64 alignment of output. +		 */ +		if ((*startp & map->mask) + size != ((*startp + size) & map->mask)) { +			unsigned int offset = *startp; +			unsigned int len = min(sizeof(*event), size), cpy; +			void *dst = map->event_copy; + +			do { +				cpy = min(map->mask + 1 - (offset & map->mask), len); +				memcpy(dst, &data[offset & map->mask], cpy); +				offset += cpy; +				dst += cpy; +				len -= cpy; +			} while (len); + +			event = (union perf_event *)map->event_copy; +		} + +		*startp += size; +	} + +	return event; +} + +/* + * Read event from ring buffer one by one. + * Return one event for each call. + * + * Usage: + * perf_mmap__read_init() + * while(event = perf_mmap__read_event()) { + *	//process the event + *	perf_mmap__consume() + * } + * perf_mmap__read_done() + */ +union perf_event *perf_mmap__read_event(struct perf_mmap *map) +{ +	union perf_event *event; + +	/* +	 * Check if event was unmapped due to a POLLHUP/POLLERR. +	 */ +	if (!refcount_read(&map->refcnt)) +		return NULL; + +	/* non-overwirte doesn't pause the ringbuffer */ +	if (!map->overwrite) +		map->end = perf_mmap__read_head(map); + +	event = perf_mmap__read(map, &map->start, map->end); + +	if (!map->overwrite) +		map->prev = map->start; + +	return event; +} diff --git a/tools/perf/lib/tests/Makefile b/tools/perf/lib/tests/Makefile index 1ee4e9ba848b..a43cd08c5c03 100644 --- a/tools/perf/lib/tests/Makefile +++ b/tools/perf/lib/tests/Makefile @@ -16,13 +16,13 @@ all:  include $(srctree)/tools/scripts/Makefile.include -INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include +INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include -I$(srctree)/tools/lib  $(TESTS_A): FORCE -	$(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a +	$(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a $(LIBAPI)  $(TESTS_SO): FORCE -	$(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) -lperf +	$(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) $(LIBAPI) -lperf  all: $(TESTS_A) $(TESTS_SO) diff --git a/tools/perf/lib/tests/test-cpumap.c b/tools/perf/lib/tests/test-cpumap.c index aa34c20df07e..c8d45091e7c2 100644 --- a/tools/perf/lib/tests/test-cpumap.c +++ b/tools/perf/lib/tests/test-cpumap.c @@ -26,6 +26,6 @@ int main(int argc, char **argv)  	perf_cpu_map__put(cpus);  	perf_cpu_map__put(cpus); -	__T_OK; +	__T_END;  	return 0;  } diff --git a/tools/perf/lib/tests/test-evlist.c b/tools/perf/lib/tests/test-evlist.c index e6b2ab2e2bde..6d8ebe0c2504 100644 --- a/tools/perf/lib/tests/test-evlist.c +++ b/tools/perf/lib/tests/test-evlist.c @@ -1,12 +1,23 @@  // SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE // needed for sched.h to get sched_[gs]etaffinity and CPU_(ZERO,SET) +#include <sched.h>  #include <stdio.h>  #include <stdarg.h> +#include <unistd.h> +#include <stdlib.h>  #include <linux/perf_event.h> +#include <linux/limits.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/prctl.h>  #include <perf/cpumap.h>  #include <perf/threadmap.h>  #include <perf/evlist.h>  #include <perf/evsel.h> +#include <perf/mmap.h> +#include <perf/event.h>  #include <internal/tests.h> +#include <api/fs/fs.h>  static int libperf_print(enum libperf_print_level level,  			 const char *fmt, va_list ap) @@ -181,6 +192,210 @@ static int test_stat_thread_enable(void)  	return 0;  } +static int test_mmap_thread(void) +{ +	struct perf_evlist *evlist; +	struct perf_evsel *evsel; +	struct perf_mmap *map; +	struct perf_cpu_map *cpus; +	struct perf_thread_map *threads; +	struct perf_event_attr attr = { +		.type             = PERF_TYPE_TRACEPOINT, +		.sample_period    = 1, +		.wakeup_watermark = 1, +		.disabled         = 1, +	}; +	char path[PATH_MAX]; +	int id, err, pid, go_pipe[2]; +	union perf_event *event; +	char bf; +	int count = 0; + +	snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", +		 sysfs__mountpoint()); + +	if (filename__read_int(path, &id)) { +		fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); +		return -1; +	} + +	attr.config = id; + +	err = pipe(go_pipe); +	__T("failed to create pipe", err == 0); + +	fflush(NULL); + +	pid = fork(); +	if (!pid) { +		int i; + +		read(go_pipe[0], &bf, 1); + +		/* Generate 100 prctl calls. */ +		for (i = 0; i < 100; i++) +			prctl(0, 0, 0, 0, 0); + +		exit(0); +	} + +	threads = perf_thread_map__new_dummy(); +	__T("failed to create threads", threads); + +	cpus = perf_cpu_map__dummy_new(); +	__T("failed to create cpus", cpus); + +	perf_thread_map__set_pid(threads, 0, pid); + +	evlist = perf_evlist__new(); +	__T("failed to create evlist", evlist); + +	evsel = perf_evsel__new(&attr); +	__T("failed to create evsel1", evsel); + +	perf_evlist__add(evlist, evsel); + +	perf_evlist__set_maps(evlist, cpus, threads); + +	err = perf_evlist__open(evlist); +	__T("failed to open evlist", err == 0); + +	err = perf_evlist__mmap(evlist, 4); +	__T("failed to mmap evlist", err == 0); + +	perf_evlist__enable(evlist); + +	/* kick the child and wait for it to finish */ +	write(go_pipe[1], &bf, 1); +	waitpid(pid, NULL, 0); + +	/* +	 * There's no need to call perf_evlist__disable, +	 * monitored process is dead now. +	 */ + +	perf_evlist__for_each_mmap(evlist, map, false) { +		if (perf_mmap__read_init(map) < 0) +			continue; + +		while ((event = perf_mmap__read_event(map)) != NULL) { +			count++; +			perf_mmap__consume(map); +		} + +		perf_mmap__read_done(map); +	} + +	/* calls perf_evlist__munmap/perf_evlist__close */ +	perf_evlist__delete(evlist); + +	perf_thread_map__put(threads); +	perf_cpu_map__put(cpus); + +	/* +	 * The generated prctl calls should match the +	 * number of events in the buffer. +	 */ +	__T("failed count", count == 100); + +	return 0; +} + +static int test_mmap_cpus(void) +{ +	struct perf_evlist *evlist; +	struct perf_evsel *evsel; +	struct perf_mmap *map; +	struct perf_cpu_map *cpus; +	struct perf_event_attr attr = { +		.type             = PERF_TYPE_TRACEPOINT, +		.sample_period    = 1, +		.wakeup_watermark = 1, +		.disabled         = 1, +	}; +	cpu_set_t saved_mask; +	char path[PATH_MAX]; +	int id, err, cpu, tmp; +	union perf_event *event; +	int count = 0; + +	snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", +		 sysfs__mountpoint()); + +	if (filename__read_int(path, &id)) { +		fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); +		return -1; +	} + +	attr.config = id; + +	cpus = perf_cpu_map__new(NULL); +	__T("failed to create cpus", cpus); + +	evlist = perf_evlist__new(); +	__T("failed to create evlist", evlist); + +	evsel = perf_evsel__new(&attr); +	__T("failed to create evsel1", evsel); + +	perf_evlist__add(evlist, evsel); + +	perf_evlist__set_maps(evlist, cpus, NULL); + +	err = perf_evlist__open(evlist); +	__T("failed to open evlist", err == 0); + +	err = perf_evlist__mmap(evlist, 4); +	__T("failed to mmap evlist", err == 0); + +	perf_evlist__enable(evlist); + +	err = sched_getaffinity(0, sizeof(saved_mask), &saved_mask); +	__T("sched_getaffinity failed", err == 0); + +	perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { +		cpu_set_t mask; + +		CPU_ZERO(&mask); +		CPU_SET(cpu, &mask); + +		err = sched_setaffinity(0, sizeof(mask), &mask); +		__T("sched_setaffinity failed", err == 0); + +		prctl(0, 0, 0, 0, 0); +	} + +	err = sched_setaffinity(0, sizeof(saved_mask), &saved_mask); +	__T("sched_setaffinity failed", err == 0); + +	perf_evlist__disable(evlist); + +	perf_evlist__for_each_mmap(evlist, map, false) { +		if (perf_mmap__read_init(map) < 0) +			continue; + +		while ((event = perf_mmap__read_event(map)) != NULL) { +			count++; +			perf_mmap__consume(map); +		} + +		perf_mmap__read_done(map); +	} + +	/* calls perf_evlist__munmap/perf_evlist__close */ +	perf_evlist__delete(evlist); + +	/* +	 * The generated prctl events should match the +	 * number of cpus or be bigger (we are system-wide). +	 */ +	__T("failed count", count >= perf_cpu_map__nr(cpus)); + +	perf_cpu_map__put(cpus); + +	return 0; +} +  int main(int argc, char **argv)  {  	__T_START; @@ -190,7 +405,9 @@ int main(int argc, char **argv)  	test_stat_cpu();  	test_stat_thread();  	test_stat_thread_enable(); +	test_mmap_thread(); +	test_mmap_cpus(); -	__T_OK; +	__T_END;  	return 0;  } diff --git a/tools/perf/lib/tests/test-evsel.c b/tools/perf/lib/tests/test-evsel.c index 1b6c4285ac2b..135722ac965b 100644 --- a/tools/perf/lib/tests/test-evsel.c +++ b/tools/perf/lib/tests/test-evsel.c @@ -130,6 +130,6 @@ int main(int argc, char **argv)  	test_stat_thread();  	test_stat_thread_enable(); -	__T_OK; +	__T_END;  	return 0;  } diff --git a/tools/perf/lib/tests/test-threadmap.c b/tools/perf/lib/tests/test-threadmap.c index 8c5f47247d9e..7dc4d6fbedde 100644 --- a/tools/perf/lib/tests/test-threadmap.c +++ b/tools/perf/lib/tests/test-threadmap.c @@ -26,6 +26,6 @@ int main(int argc, char **argv)  	perf_thread_map__put(threads);  	perf_thread_map__put(threads); -	__T_OK; +	__T_END;  	return 0;  }  |