2798 lines · 64.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> * * Parts came from builtin-{top,stat,record}.c, see those files for further * copyright notes. */#include "evlist.h" #include <errno.h>#include <inttypes.h>#include <signal.h>#include <stdlib.h>#include <string.h> #include <fcntl.h>#include <linux/bitops.h>#include <linux/err.h>#include <linux/hash.h>#include <linux/log2.h>#include <linux/string.h>#include <linux/time64.h>#include <linux/zalloc.h>#include <poll.h>#include <sched.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <sys/prctl.h>#include <sys/timerfd.h>#include <sys/wait.h>#include <unistd.h> #include <api/fs/fs.h>#include <internal/lib.h> // page_size#include <internal/rc_check.h>#include <internal/xyarray.h>#include <perf/cpumap.h>#include <perf/evlist.h>#include <perf/evsel.h>#include <perf/mmap.h>#include <subcmd/parse-options.h> #include "../perf.h"#include "affinity.h"#include "asm/bug.h"#include "bpf-event.h"#include "bpf-filter.h"#include "bpf_counter.h"#include "cpumap.h"#include "debug.h"#include "dwarf-regs.h"#include "env.h"#include "event.h"#include "evsel.h"#include "evsel_fprintf.h"#include "intel-tpebs.h"#include "metricgroup.h"#include "mmap.h"#include "parse-events.h"#include "perf_api_probe.h"#include "pmu.h"#include "pmus.h"#include "record.h"#include "sample.h"#include "stat.h"#include "strbuf.h"#include "string2.h"#include "target.h"#include "thread_map.h"#include "units.h"#include "util.h" #ifdef LACKS_SIGQUEUE_PROTOTYPEint sigqueue(pid_t pid, int sig, const union sigval value);#endif #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))#define SID(e, x, y) xyarray__entry(e->core.sample_id, x, y) static void event_enable_timer__exit(struct event_enable_timer **ep); struct evlist *evlist__new(void){	struct evlist *result;	RC_STRUCT(evlist) *evlist; 	evlist = zalloc(sizeof(*evlist));	if (ADD_RC_CHK(result, evlist)) {		perf_evlist__init(evlist__core(result));		perf_evlist__set_maps(evlist__core(result), /*cpus=*/NULL, /*threads=*/NULL);		evlist__set_workload_pid(result, -1);		evlist__set_bkw_mmap_state(result, BKW_MMAP_NOTREADY);		evlist__set_ctl_fd_fd(result, -1);		evlist__set_ctl_fd_ack(result, -1);		evlist__set_ctl_fd_pos(result, -1);		evlist__set_nr_br_cntr(result, -1);		metricgroup__rblist_init(evlist__metric_events(result));		INIT_LIST_HEAD(&evlist->deferred_samples);		refcount_set(evlist__refcnt(result), 1);	} else {		free(evlist);		result = NULL;	}	return result;} struct evlist *evlist__new_default(const struct target *target, bool sample_callchains){	struct evlist *evlist = evlist__new();	bool can_profile_kernel;	struct perf_pmu *pmu = NULL;	char buf[256];	int err; 	if (!evlist)		return NULL; 	can_profile_kernel = perf_event_paranoid_check(1); 	if (EM_HOST == EM_S390 && sample_callchains) {		snprintf(buf, sizeof(buf), "software/%s/%s",			 target__has_cpu(target) ? "cpu-clock" : "task-clock",			 can_profile_kernel ? "P" : "Pu");		err = parse_event(evlist, buf);		if (err)			goto out_err;	} else {		while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {			snprintf(buf, sizeof(buf), "%s/cycles/%s", pmu->name,				can_profile_kernel ? "P" : "Pu");			err = parse_event(evlist, buf);			if (err)				goto out_err;		}	} 	/* If there is only 1 event a sample identifier isn't necessary. */	if (evlist__nr_entries(evlist) > 1) {		struct evsel *evsel; 		evlist__for_each_entry(evlist, evsel)			evsel__set_sample_id(evsel, /*can_sample_identifier=*/false);	} 	return evlist;out_err:	evlist__put(evlist);	return NULL;} struct evlist *evlist__new_dummy(void){	struct evlist *evlist = evlist__new(); 	if (evlist && evlist__add_dummy(evlist)) {		evlist__put(evlist);		evlist = NULL;	} 	return evlist;} struct evlist *evlist__get(struct evlist *evlist){	struct evlist *result; 	if (RC_CHK_GET(result, evlist))		refcount_inc(evlist__refcnt(evlist)); 	return result;} /** * evlist__set_id_pos - set the positions of event ids. * @evlist: selected event list * * Events with compatible sample types all have the same id_pos * and is_pos.  For convenience, put a copy on evlist. */void evlist__set_id_pos(struct evlist *evlist){	struct evsel *first = evlist__first(evlist); 	RC_CHK_ACCESS(evlist)->id_pos =  first->id_pos;	RC_CHK_ACCESS(evlist)->is_pos =  first->is_pos;} static void evlist__update_id_pos(struct evlist *evlist){	struct evsel *evsel; 	evlist__for_each_entry(evlist, evsel)		evsel__calc_id_pos(evsel); 	evlist__set_id_pos(evlist);} static void evlist__purge(struct evlist *evlist){	struct evsel *pos, *n; 	evlist__for_each_entry_safe(evlist, n, pos) {		list_del_init(&pos->core.node);		if (pos->evlist) {			if (!RC_CHK_EQUAL(pos->evlist, evlist)) {				evlist__put(pos->evlist);			} else {				refcount_dec_and_test(evlist__refcnt(pos->evlist));				RC_CHK_PUT(pos->evlist);			}		}		pos->evlist = NULL;		evsel__put(pos);	} 	evlist__core(evlist)->nr_entries = 0;} static void evlist__exit(struct evlist *evlist){	metricgroup__rblist_exit(evlist__metric_events(evlist));	event_enable_timer__exit(&RC_CHK_ACCESS(evlist)->eet);	free(evlist__mmap(evlist));	free(evlist__overwrite_mmap(evlist));	perf_evlist__exit(evlist__core(evlist));} void evlist__put(struct evlist *evlist){	struct evsel *evsel;	unsigned int count, ref_cnt; 	if (evlist == NULL)		return; 	if (refcount_dec_and_test(evlist__refcnt(evlist)))		goto out_delete; retry:	count = refcount_read(evlist__refcnt(evlist));	ref_cnt = count;	evlist__for_each_entry(evlist, evsel) {		if (RC_CHK_EQUAL(evsel->evlist, evlist) && count &&		    refcount_read(&evsel->refcnt) == 1)			count--;	}	if (refcount_read(evlist__refcnt(evlist)) != ref_cnt)		goto retry; 	if (count != 0) {		/*		 * Not the last reference except for back references from		 * evsels.		 */		RC_CHK_PUT(evlist);		return;	}out_delete:	evlist__free_stats(evlist);	evlist__do_munmap(evlist);	evlist__close(evlist);	evlist__purge(evlist);	evlist__exit(evlist);	RC_CHK_FREE(evlist);} void evlist__add(struct evlist *evlist, struct evsel *entry){	perf_evlist__add(evlist__core(evlist), &entry->core);	evlist__put(entry->evlist);	entry->evlist = evlist__get(evlist);	entry->tracking = !entry->core.idx; 	if (evlist__nr_entries(evlist) == 1)		evlist__set_id_pos(evlist);} void evlist__remove(struct evlist *evlist, struct evsel *evsel){	perf_evlist__remove(evlist__core(evlist), &evsel->core);	evlist__put(evsel->evlist);	evsel->evlist = NULL;} void evlist__splice_list_tail(struct evlist *evlist, struct list_head *list){	while (!list_empty(list)) {		struct evsel *evsel, *temp, *leader = NULL; 		__evlist__for_each_entry_safe(list, temp, evsel) {			list_del_init(&evsel->core.node);			evlist__add(evlist, evsel);			leader = evsel;			break;		} 		__evlist__for_each_entry_safe(list, temp, evsel) {			if (evsel__has_leader(evsel, leader)) {				list_del_init(&evsel->core.node);				evlist__add(evlist, evsel);			}		}	}} int __evlist__set_tracepoints_handlers(struct evlist *evlist,				       const struct evsel_str_handler *assocs, size_t nr_assocs){	size_t i;	int err; 	for (i = 0; i < nr_assocs; i++) {		// Adding a handler for an event not in this evlist, just ignore it.		struct evsel *evsel = evlist__find_tracepoint_by_name(evlist, assocs[i].name);		if (evsel == NULL)			continue; 		err = -EEXIST;		if (evsel->handler != NULL)			goto out;		evsel->handler = assocs[i].handler;	} 	err = 0;out:	return err;} static void evlist__set_leader(struct evlist *evlist){	perf_evlist__set_leader(evlist__core(evlist));} static struct evsel *evlist__dummy_event(struct evlist *evlist){	struct perf_event_attr attr = {		.type	= PERF_TYPE_SOFTWARE,		.config = PERF_COUNT_SW_DUMMY,		.size	= sizeof(attr), /* to capture ABI version */		/* Avoid frequency mode for dummy events to avoid associated timers. */		.freq = 0,		.sample_period = 1,	}; 	return evsel__new_idx(&attr, evlist__nr_entries(evlist));} int evlist__add_dummy(struct evlist *evlist){	struct evsel *evsel = evlist__dummy_event(evlist); 	if (evsel == NULL)		return -ENOMEM; 	evlist__add(evlist, evsel);	return 0;} struct evsel *evlist__add_aux_dummy(struct evlist *evlist, bool system_wide){	struct evsel *evsel = evlist__dummy_event(evlist); 	if (!evsel)		return NULL; 	evsel->core.attr.exclude_kernel = 1;	evsel->core.attr.exclude_guest = 1;	evsel->core.attr.exclude_hv = 1;	evsel->core.system_wide = system_wide;	evsel->no_aux_samples = true;	evsel->name = strdup("dummy:u"); 	evlist__add(evlist, evsel);	return evsel;} #ifdef HAVE_LIBTRACEEVENTstruct evsel *evlist__add_sched_switch(struct evlist *evlist, bool system_wide){	struct evsel *evsel = evsel__newtp_idx("sched", "sched_switch", 0,					       /*format=*/true); 	if (IS_ERR(evsel))		return evsel; 	evsel__set_sample_bit(evsel, CPU);	evsel__set_sample_bit(evsel, TIME); 	evsel->core.system_wide = system_wide;	evsel->no_aux_samples = true; 	evlist__add(evlist, evsel);	return evsel;}#endif struct evsel *evlist__find_tracepoint_by_name(struct evlist *evlist, const char *name){	struct evsel *evsel; 	evlist__for_each_entry(evlist, evsel) {		if ((evsel->core.attr.type == PERF_TYPE_TRACEPOINT) &&		    (strcmp(evsel->name, name) == 0))			return evsel;	} 	return NULL;} #ifdef HAVE_LIBTRACEEVENTint evlist__add_newtp(struct evlist *evlist, const char *sys, const char *name, void *handler){	struct evsel *evsel = evsel__newtp(sys, name); 	if (IS_ERR(evsel))		return -1; 	evsel->handler = handler;	evlist__add(evlist, evsel);	return 0;}#endif /* * Should sched_setaffinity be used with evlist__for_each_cpu? Determine if * migrating the thread will avoid possibly numerous IPIs. */static bool evlist__use_affinity(struct evlist *evlist){	struct evsel *pos;	struct perf_cpu_map *used_cpus = NULL;	bool ret = false; 	if (evlist__no_affinity(evlist) || !evlist__core(evlist)->user_requested_cpus ||	    cpu_map__is_dummy(evlist__core(evlist)->user_requested_cpus))		return false; 	evlist__for_each_entry(evlist, pos) {		struct perf_cpu_map *intersect; 		if (!perf_pmu__benefits_from_affinity(pos->pmu))			continue; 		if (evsel__is_dummy_event(pos)) {			/*			 * The dummy event is opened on all CPUs so assume >1			 * event with shared CPUs.			 */			ret = true;			break;		}		if (evsel__is_retire_lat(pos)) {			/*			 * Retirement latency events are similar to tool ones in			 * their implementation, and so don't require affinity.			 */			continue;		}		if (perf_cpu_map__is_empty(used_cpus)) {			/* First benefitting event, we want >1 on a common CPU. */			used_cpus = perf_cpu_map__get(pos->core.cpus);			continue;		}		if ((pos->core.attr.read_format & PERF_FORMAT_GROUP) &&		    evsel__leader(pos) != pos) {			/* Skip members of the same sample group. */			continue;		}		intersect = perf_cpu_map__intersect(used_cpus, pos->core.cpus);		if (!perf_cpu_map__is_empty(intersect)) {			/* >1 event with shared CPUs. */			perf_cpu_map__put(intersect);			ret = true;			break;		}		perf_cpu_map__put(intersect);		perf_cpu_map__merge(&used_cpus, pos->core.cpus);	}	perf_cpu_map__put(used_cpus);	return ret;} void evlist_cpu_iterator__init(struct evlist_cpu_iterator *itr, struct evlist *evlist){	*itr = (struct evlist_cpu_iterator){		.container = evlist,		.evsel = NULL,		.cpu_map_idx = 0,		.evlist_cpu_map_idx = 0,		.evlist_cpu_map_nr = perf_cpu_map__nr(evlist__core(evlist)->all_cpus),		.cpu = (struct perf_cpu){ .cpu = -1},		.affinity = NULL,	}; 	if (evlist__empty(evlist)) {		/* Ensure the empty list doesn't iterate. */		itr->evlist_cpu_map_idx = itr->evlist_cpu_map_nr;		return;	} 

Lines 1–500 of 2798.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t