618 lines · 13.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <errno.h>#include <unistd.h>#include <sys/syscall.h>#include <perf/evsel.h>#include <perf/cpumap.h>#include <perf/threadmap.h>#include <linux/hash.h>#include <linux/list.h>#include <internal/evsel.h>#include <linux/zalloc.h>#include <stdlib.h>#include <internal/xyarray.h>#include <internal/cpumap.h>#include <internal/mmap.h>#include <internal/threadmap.h>#include <internal/lib.h>#include <linux/string.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <asm/bug.h> void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr,		      int idx){	INIT_LIST_HEAD(&evsel->node);	INIT_LIST_HEAD(&evsel->per_stream_periods);	evsel->attr = *attr;	evsel->idx  = idx;	evsel->leader = evsel;} struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr){	struct perf_evsel *evsel = zalloc(sizeof(*evsel)); 	if (evsel != NULL)		perf_evsel__init(evsel, attr, 0); 	return evsel;} void perf_evsel__exit(struct perf_evsel *evsel){	assert(evsel->fd == NULL);  /* If not fds were not closed. */	assert(evsel->mmap == NULL); /* If not munmap wasn't called. */	assert(evsel->sample_id == NULL); /* If not free_id wasn't called. */	perf_cpu_map__put(evsel->cpus);	perf_cpu_map__put(evsel->pmu_cpus);	perf_thread_map__put(evsel->threads);} void perf_evsel__delete(struct perf_evsel *evsel){	perf_evsel__exit(evsel);	free(evsel);} #define FD(_evsel, _cpu_map_idx, _thread)				\	((int *)xyarray__entry(_evsel->fd, _cpu_map_idx, _thread))#define MMAP(_evsel, _cpu_map_idx, _thread)				\	(_evsel->mmap ? ((struct perf_mmap *) xyarray__entry(_evsel->mmap, _cpu_map_idx, _thread)) \		      : NULL) int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads){	evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); 	if (evsel->fd) {		int idx, thread; 		for (idx = 0; idx < ncpus; idx++) {			for (thread = 0; thread < nthreads; thread++) {				int *fd = FD(evsel, idx, thread); 				if (fd)					*fd = -1;			}		}	} 	return evsel->fd != NULL ? 0 : -ENOMEM;} static int perf_evsel__alloc_mmap(struct perf_evsel *evsel, int ncpus, int nthreads){	evsel->mmap = xyarray__new(ncpus, nthreads, sizeof(struct perf_mmap)); 	return evsel->mmap != NULL ? 0 : -ENOMEM;} static intsys_perf_event_open(struct perf_event_attr *attr,		    pid_t pid, struct perf_cpu cpu, int group_fd,		    unsigned long flags){	return syscall(__NR_perf_event_open, attr, pid, cpu.cpu, group_fd, flags);} static int get_group_fd(struct perf_evsel *evsel, int cpu_map_idx, int thread, int *group_fd){	struct perf_evsel *leader = evsel->leader;	int *fd; 	if (evsel == leader) {		*group_fd = -1;		return 0;	} 	/*	 * Leader must be already processed/open,	 * if not it's a bug.	 */	if (!leader->fd)		return -ENOTCONN; 	fd = FD(leader, cpu_map_idx, thread);	if (fd == NULL || *fd == -1)		return -EBADF; 	*group_fd = *fd; 	return 0;} int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus,		     struct perf_thread_map *threads){	struct perf_cpu cpu;	unsigned int idx;	int thread, err = 0; 	if (cpus == NULL) {		static struct perf_cpu_map *empty_cpu_map; 		if (empty_cpu_map == NULL) {			empty_cpu_map = perf_cpu_map__new_any_cpu();			if (empty_cpu_map == NULL)				return -ENOMEM;		} 		cpus = empty_cpu_map;	} 	if (threads == NULL) {		static struct perf_thread_map *empty_thread_map; 		if (empty_thread_map == NULL) {			empty_thread_map = perf_thread_map__new_dummy();			if (empty_thread_map == NULL)				return -ENOMEM;		} 		threads = empty_thread_map;	} 	if (evsel->fd == NULL &&	    perf_evsel__alloc_fd(evsel, perf_cpu_map__nr(cpus), threads->nr) < 0)		return -ENOMEM; 	perf_cpu_map__for_each_cpu(cpu, idx, cpus) {		for (thread = 0; thread < threads->nr; thread++) {			int fd, group_fd, *evsel_fd; 			evsel_fd = FD(evsel, idx, thread);			if (evsel_fd == NULL) {				err = -EINVAL;				goto out;			} 			err = get_group_fd(evsel, idx, thread, &group_fd);			if (err < 0)				goto out; 			fd = sys_perf_event_open(&evsel->attr,						 threads->map[thread].pid,						 cpu, group_fd, 0); 			if (fd < 0) {				err = -errno;				goto out;			} 			*evsel_fd = fd;		}	}out:	if (err)		perf_evsel__close(evsel); 	return err;} static void perf_evsel__close_fd_cpu(struct perf_evsel *evsel, int cpu_map_idx){	int thread; 	for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) {		int *fd = FD(evsel, cpu_map_idx, thread); 		if (fd && *fd >= 0) {			close(*fd);			*fd = -1;		}	}} void perf_evsel__close_fd(struct perf_evsel *evsel){	for (int idx = 0; idx < xyarray__max_x(evsel->fd); idx++)		perf_evsel__close_fd_cpu(evsel, idx);} void perf_evsel__free_fd(struct perf_evsel *evsel){	xyarray__delete(evsel->fd);	evsel->fd = NULL;} void perf_evsel__close(struct perf_evsel *evsel){	if (evsel->fd == NULL)		return; 	perf_evsel__close_fd(evsel);	perf_evsel__free_fd(evsel);} void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu_map_idx){	if (evsel->fd == NULL)		return; 	perf_evsel__close_fd_cpu(evsel, cpu_map_idx);} void perf_evsel__munmap(struct perf_evsel *evsel){	int idx, thread; 	if (evsel->fd == NULL || evsel->mmap == NULL)		return; 	for (idx = 0; idx < xyarray__max_x(evsel->fd); idx++) {		for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {			int *fd = FD(evsel, idx, thread); 			if (fd == NULL || *fd < 0)				continue; 			perf_mmap__munmap(MMAP(evsel, idx, thread));		}	} 	xyarray__delete(evsel->mmap);	evsel->mmap = NULL;} int perf_evsel__mmap(struct perf_evsel *evsel, int pages){	int ret, idx, thread;	struct perf_mmap_param mp = {		.prot = PROT_READ | PROT_WRITE,		.mask = (pages * page_size) - 1,	}; 	if (evsel->fd == NULL || evsel->mmap)		return -EINVAL; 	if (perf_evsel__alloc_mmap(evsel, xyarray__max_x(evsel->fd), xyarray__max_y(evsel->fd)) < 0)		return -ENOMEM; 	for (idx = 0; idx < xyarray__max_x(evsel->fd); idx++) {		for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {			int *fd = FD(evsel, idx, thread);			struct perf_mmap *map;			struct perf_cpu cpu = perf_cpu_map__cpu(evsel->cpus, idx); 			if (fd == NULL || *fd < 0)				continue; 			map = MMAP(evsel, idx, thread);			perf_mmap__init(map, NULL, false, NULL); 			ret = perf_mmap__mmap(map, &mp, *fd, cpu);			if (ret) {				perf_evsel__munmap(evsel);				return ret;			}		}	} 	return 0;} void *perf_evsel__mmap_base(struct perf_evsel *evsel, int cpu_map_idx, int thread){	int *fd = FD(evsel, cpu_map_idx, thread); 	if (fd == NULL || *fd < 0 || MMAP(evsel, cpu_map_idx, thread) == NULL)		return NULL; 	return MMAP(evsel, cpu_map_idx, thread)->base;} int perf_evsel__read_size(struct perf_evsel *evsel){	u64 read_format = evsel->attr.read_format;	int entry = sizeof(u64); /* value */	int size = 0;	int nr = 1; 	if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)		size += sizeof(u64); 	if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)		size += sizeof(u64); 	if (read_format & PERF_FORMAT_ID)		entry += sizeof(u64); 	if (read_format & PERF_FORMAT_LOST)		entry += sizeof(u64); 	if (read_format & PERF_FORMAT_GROUP) {		nr = evsel->nr_members;		size += sizeof(u64);	} 	size += entry * nr;	return size;} /* This only reads values for the leader */static int perf_evsel__read_group(struct perf_evsel *evsel, int cpu_map_idx,				  int thread, struct perf_counts_values *count){	size_t size = perf_evsel__read_size(evsel);	int *fd = FD(evsel, cpu_map_idx, thread);	u64 read_format = evsel->attr.read_format;	u64 *data;	int idx = 1; 	if (fd == NULL || *fd < 0)		return -EINVAL; 	data = calloc(1, size);	if (data == NULL)		return -ENOMEM; 	if (readn(*fd, data, size) <= 0) {		free(data);		return -errno;	} 	/*	 * This reads only the leader event intentionally since we don't have	 * perf counts values for sibling events.	 */	if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)		count->ena = data[idx++];	if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)		count->run = data[idx++]; 	/* value is always available */	count->val = data[idx++];	if (read_format & PERF_FORMAT_ID)		count->id = data[idx++];	if (read_format & PERF_FORMAT_LOST)		count->lost = data[idx++]; 	free(data);	return 0;} /* * The perf read format is very flexible.  It needs to set the proper * values according to the read format. */static void perf_evsel__adjust_values(struct perf_evsel *evsel, u64 *buf,				      struct perf_counts_values *count){	u64 read_format = evsel->attr.read_format;	int n = 0; 	count->val = buf[n++]; 	if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)		count->ena = buf[n++]; 	if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)		count->run = buf[n++]; 	if (read_format & PERF_FORMAT_ID)		count->id = buf[n++]; 	if (read_format & PERF_FORMAT_LOST)		count->lost = buf[n++];} int perf_evsel__read(struct perf_evsel *evsel, int cpu_map_idx, int thread,		     struct perf_counts_values *count){	size_t size = perf_evsel__read_size(evsel);	int *fd = FD(evsel, cpu_map_idx, thread);	u64 read_format = evsel->attr.read_format;	struct perf_counts_values buf; 	memset(count, 0, sizeof(*count)); 	if (fd == NULL || *fd < 0)		return -EINVAL; 	if (read_format & PERF_FORMAT_GROUP)		return perf_evsel__read_group(evsel, cpu_map_idx, thread, count); 	if (MMAP(evsel, cpu_map_idx, thread) &&	    !(read_format & (PERF_FORMAT_ID | PERF_FORMAT_LOST)) &&	    !perf_mmap__read_self(MMAP(evsel, cpu_map_idx, thread), count))		return 0; 	if (readn(*fd, buf.values, size) <= 0)		return -errno; 	perf_evsel__adjust_values(evsel, buf.values, count);	return 0;} static int perf_evsel__ioctl(struct perf_evsel *evsel, int ioc, void *arg,			     int cpu_map_idx, int thread){	int *fd = FD(evsel, cpu_map_idx, thread); 	if (fd == NULL || *fd < 0)		return -1; 	return ioctl(*fd, ioc, arg);} static int perf_evsel__run_ioctl(struct perf_evsel *evsel,				 int ioc,  void *arg,				 int cpu_map_idx){	int thread; 	for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {		int err = perf_evsel__ioctl(evsel, ioc, arg, cpu_map_idx, thread); 		if (err)			return err;	} 	return 0;} int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu_map_idx){	return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, cpu_map_idx);} int perf_evsel__enable_thread(struct perf_evsel *evsel, int thread){	struct perf_cpu cpu __maybe_unused;	unsigned int idx;	int err; 	perf_cpu_map__for_each_cpu(cpu, idx, evsel->cpus) {		err = perf_evsel__ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, idx, thread);		if (err)			return err;	} 	return 0;} int perf_evsel__enable(struct perf_evsel *evsel){	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_map_idx){	return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, cpu_map_idx);} int perf_evsel__disable(struct perf_evsel *evsel){	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;} 

Lines 1–500 of 618.