brintos

brintos / linux-shallow public Read only

0
0
Text · 18.2 KiB · 52109af Raw
737 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/list.h>3#include <linux/list_sort.h>4#include <linux/string.h>5#include <linux/zalloc.h>6#include <subcmd/pager.h>7#include <sys/types.h>8#include <ctype.h>9#include <dirent.h>10#include <pthread.h>11#include <string.h>12#include <unistd.h>13#include "cpumap.h"14#include "debug.h"15#include "evsel.h"16#include "pmus.h"17#include "pmu.h"18#include "print-events.h"19#include "strbuf.h"20 21/*22 * core_pmus:  A PMU belongs to core_pmus if it's name is "cpu" or it's sysfs23 *             directory contains "cpus" file. All PMUs belonging to core_pmus24 *             must have pmu->is_core=1. If there are more than one PMU in25 *             this list, perf interprets it as a heterogeneous platform.26 *             (FWIW, certain ARM platforms having heterogeneous cores uses27 *             homogeneous PMU, and thus they are treated as homogeneous28 *             platform by perf because core_pmus will have only one entry)29 * other_pmus: All other PMUs which are not part of core_pmus list. It doesn't30 *             matter whether PMU is present per SMT-thread or outside of the31 *             core in the hw. For e.g., an instance of AMD ibs_fetch// and32 *             ibs_op// PMUs is present in each hw SMT thread, however they33 *             are captured under other_pmus. PMUs belonging to other_pmus34 *             must have pmu->is_core=0 but pmu->is_uncore could be 0 or 1.35 */36static LIST_HEAD(core_pmus);37static LIST_HEAD(other_pmus);38static bool read_sysfs_core_pmus;39static bool read_sysfs_all_pmus;40 41static void pmu_read_sysfs(bool core_only);42 43size_t pmu_name_len_no_suffix(const char *str)44{45	int orig_len, len;46	bool has_hex_digits = false;47 48	orig_len = len = strlen(str);49 50	/* Count trailing digits. */51	while (len > 0 && isxdigit(str[len - 1])) {52		if (!isdigit(str[len - 1]))53			has_hex_digits = true;54		len--;55	}56 57	if (len > 0 && len != orig_len && str[len - 1] == '_') {58		/*59		 * There is a '_{num}' suffix. For decimal suffixes any length60		 * will do, for hexadecimal ensure more than 2 hex digits so61		 * that S390's cpum_cf PMU doesn't match.62		 */63		if (!has_hex_digits || (orig_len - len) > 2)64			return len - 1;65	}66	/* Use the full length. */67	return orig_len;68}69 70int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name)71{72	unsigned long long lhs_num = 0, rhs_num = 0;73	size_t lhs_pmu_name_len = pmu_name_len_no_suffix(lhs_pmu_name);74	size_t rhs_pmu_name_len = pmu_name_len_no_suffix(rhs_pmu_name);75	int ret = strncmp(lhs_pmu_name, rhs_pmu_name,76			lhs_pmu_name_len < rhs_pmu_name_len ? lhs_pmu_name_len : rhs_pmu_name_len);77 78	if (lhs_pmu_name_len != rhs_pmu_name_len || ret != 0 || lhs_pmu_name_len == 0)79		return ret;80 81	if (lhs_pmu_name_len + 1 < strlen(lhs_pmu_name))82		lhs_num = strtoull(&lhs_pmu_name[lhs_pmu_name_len + 1], NULL, 16);83	if (rhs_pmu_name_len + 1 < strlen(rhs_pmu_name))84		rhs_num = strtoull(&rhs_pmu_name[rhs_pmu_name_len + 1], NULL, 16);85 86	return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0);87}88 89void perf_pmus__destroy(void)90{91	struct perf_pmu *pmu, *tmp;92 93	list_for_each_entry_safe(pmu, tmp, &core_pmus, list) {94		list_del(&pmu->list);95 96		perf_pmu__delete(pmu);97	}98	list_for_each_entry_safe(pmu, tmp, &other_pmus, list) {99		list_del(&pmu->list);100 101		perf_pmu__delete(pmu);102	}103	read_sysfs_core_pmus = false;104	read_sysfs_all_pmus = false;105}106 107static struct perf_pmu *pmu_find(const char *name)108{109	struct perf_pmu *pmu;110 111	list_for_each_entry(pmu, &core_pmus, list) {112		if (!strcmp(pmu->name, name) ||113		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))114			return pmu;115	}116	list_for_each_entry(pmu, &other_pmus, list) {117		if (!strcmp(pmu->name, name) ||118		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))119			return pmu;120	}121 122	return NULL;123}124 125struct perf_pmu *perf_pmus__find(const char *name)126{127	struct perf_pmu *pmu;128	int dirfd;129	bool core_pmu;130 131	/*132	 * Once PMU is loaded it stays in the list,133	 * so we keep us from multiple reading/parsing134	 * the pmu format definitions.135	 */136	pmu = pmu_find(name);137	if (pmu)138		return pmu;139 140	if (read_sysfs_all_pmus)141		return NULL;142 143	core_pmu = is_pmu_core(name);144	if (core_pmu && read_sysfs_core_pmus)145		return NULL;146 147	dirfd = perf_pmu__event_source_devices_fd();148	pmu = perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name,149			       /*eager_load=*/false);150	close(dirfd);151 152	if (!pmu) {153		/*154		 * Looking up an inidividual PMU failed. This may mean name is155		 * an alias, so read the PMUs from sysfs and try to find again.156		 */157		pmu_read_sysfs(core_pmu);158		pmu = pmu_find(name);159	}160	return pmu;161}162 163static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)164{165	struct perf_pmu *pmu;166	bool core_pmu;167 168	/*169	 * Once PMU is loaded it stays in the list,170	 * so we keep us from multiple reading/parsing171	 * the pmu format definitions.172	 */173	pmu = pmu_find(name);174	if (pmu)175		return pmu;176 177	if (read_sysfs_all_pmus)178		return NULL;179 180	core_pmu = is_pmu_core(name);181	if (core_pmu && read_sysfs_core_pmus)182		return NULL;183 184	return perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name,185				/*eager_load=*/false);186}187 188static int pmus_cmp(void *priv __maybe_unused,189		    const struct list_head *lhs, const struct list_head *rhs)190{191	struct perf_pmu *lhs_pmu = container_of(lhs, struct perf_pmu, list);192	struct perf_pmu *rhs_pmu = container_of(rhs, struct perf_pmu, list);193 194	return pmu_name_cmp(lhs_pmu->name ?: "", rhs_pmu->name ?: "");195}196 197/* Add all pmus in sysfs to pmu list: */198static void pmu_read_sysfs(bool core_only)199{200	int fd;201	DIR *dir;202	struct dirent *dent;203 204	if (read_sysfs_all_pmus || (core_only && read_sysfs_core_pmus))205		return;206 207	fd = perf_pmu__event_source_devices_fd();208	if (fd < 0)209		return;210 211	dir = fdopendir(fd);212	if (!dir) {213		close(fd);214		return;215	}216 217	while ((dent = readdir(dir))) {218		if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))219			continue;220		if (core_only && !is_pmu_core(dent->d_name))221			continue;222		/* add to static LIST_HEAD(core_pmus) or LIST_HEAD(other_pmus): */223		perf_pmu__find2(fd, dent->d_name);224	}225 226	closedir(dir);227	if (list_empty(&core_pmus)) {228		if (!perf_pmu__create_placeholder_core_pmu(&core_pmus))229			pr_err("Failure to set up any core PMUs\n");230	}231	list_sort(NULL, &core_pmus, pmus_cmp);232	list_sort(NULL, &other_pmus, pmus_cmp);233	if (!list_empty(&core_pmus)) {234		read_sysfs_core_pmus = true;235		if (!core_only)236			read_sysfs_all_pmus = true;237	}238}239 240static struct perf_pmu *__perf_pmus__find_by_type(unsigned int type)241{242	struct perf_pmu *pmu;243 244	list_for_each_entry(pmu, &core_pmus, list) {245		if (pmu->type == type)246			return pmu;247	}248 249	list_for_each_entry(pmu, &other_pmus, list) {250		if (pmu->type == type)251			return pmu;252	}253	return NULL;254}255 256struct perf_pmu *perf_pmus__find_by_type(unsigned int type)257{258	struct perf_pmu *pmu = __perf_pmus__find_by_type(type);259 260	if (pmu || read_sysfs_all_pmus)261		return pmu;262 263	pmu_read_sysfs(/*core_only=*/false);264	pmu = __perf_pmus__find_by_type(type);265	return pmu;266}267 268/*269 * pmu iterator: If pmu is NULL, we start at the begin, otherwise return the270 * next pmu. Returns NULL on end.271 */272struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu)273{274	bool use_core_pmus = !pmu || pmu->is_core;275 276	if (!pmu) {277		pmu_read_sysfs(/*core_only=*/false);278		pmu = list_prepare_entry(pmu, &core_pmus, list);279	}280	if (use_core_pmus) {281		list_for_each_entry_continue(pmu, &core_pmus, list)282			return pmu;283 284		pmu = NULL;285		pmu = list_prepare_entry(pmu, &other_pmus, list);286	}287	list_for_each_entry_continue(pmu, &other_pmus, list)288		return pmu;289	return NULL;290}291 292struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu)293{294	if (!pmu) {295		pmu_read_sysfs(/*core_only=*/true);296		return list_first_entry_or_null(&core_pmus, typeof(*pmu), list);297	}298	list_for_each_entry_continue(pmu, &core_pmus, list)299		return pmu;300 301	return NULL;302}303 304static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu)305{306	bool use_core_pmus = !pmu || pmu->is_core;307	int last_pmu_name_len = 0;308	const char *last_pmu_name = (pmu && pmu->name) ? pmu->name : "";309 310	if (!pmu) {311		pmu_read_sysfs(/*core_only=*/false);312		pmu = list_prepare_entry(pmu, &core_pmus, list);313	} else314		last_pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");315 316	if (use_core_pmus) {317		list_for_each_entry_continue(pmu, &core_pmus, list) {318			int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");319 320			if (last_pmu_name_len == pmu_name_len &&321			    !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len))322				continue;323 324			return pmu;325		}326		pmu = NULL;327		pmu = list_prepare_entry(pmu, &other_pmus, list);328	}329	list_for_each_entry_continue(pmu, &other_pmus, list) {330		int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");331 332		if (last_pmu_name_len == pmu_name_len &&333		    !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len))334			continue;335 336		return pmu;337	}338	return NULL;339}340 341const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)342{343	struct perf_pmu *pmu = NULL;344 345	while ((pmu = perf_pmus__scan(pmu)) != NULL) {346		if (!strcmp(pmu->name, str))347			return pmu;348		/* Ignore "uncore_" prefix. */349		if (!strncmp(pmu->name, "uncore_", 7)) {350			if (!strcmp(pmu->name + 7, str))351				return pmu;352		}353		/* Ignore "cpu_" prefix on Intel hybrid PMUs. */354		if (!strncmp(pmu->name, "cpu_", 4)) {355			if (!strcmp(pmu->name + 4, str))356				return pmu;357		}358	}359	return NULL;360}361 362/** Struct for ordering events as output in perf list. */363struct sevent {364	/** PMU for event. */365	const struct perf_pmu *pmu;366	const char *name;367	const char* alias;368	const char *scale_unit;369	const char *desc;370	const char *long_desc;371	const char *encoding_desc;372	const char *topic;373	const char *pmu_name;374	const char *event_type_desc;375	bool deprecated;376};377 378static int cmp_sevent(const void *a, const void *b)379{380	const struct sevent *as = a;381	const struct sevent *bs = b;382	bool a_iscpu, b_iscpu;383	int ret;384 385	/* Put extra events last. */386	if (!!as->desc != !!bs->desc)387		return !!as->desc - !!bs->desc;388 389	/* Order by topics. */390	ret = strcmp(as->topic ?: "", bs->topic ?: "");391	if (ret)392		return ret;393 394	/* Order CPU core events to be first */395	a_iscpu = as->pmu ? as->pmu->is_core : true;396	b_iscpu = bs->pmu ? bs->pmu->is_core : true;397	if (a_iscpu != b_iscpu)398		return a_iscpu ? -1 : 1;399 400	/* Order by PMU name. */401	if (as->pmu != bs->pmu) {402		ret = strcmp(as->pmu_name ?: "", bs->pmu_name ?: "");403		if (ret)404			return ret;405	}406 407	/* Order by event name. */408	return strcmp(as->name, bs->name);409}410 411static bool pmu_alias_is_duplicate(struct sevent *a, struct sevent *b)412{413	/* Different names -> never duplicates */414	if (strcmp(a->name ?: "//", b->name ?: "//"))415		return false;416 417	/* Don't remove duplicates for different PMUs */418	return strcmp(a->pmu_name, b->pmu_name) == 0;419}420 421struct events_callback_state {422	struct sevent *aliases;423	size_t aliases_len;424	size_t index;425};426 427static int perf_pmus__print_pmu_events__callback(void *vstate,428						struct pmu_event_info *info)429{430	struct events_callback_state *state = vstate;431	struct sevent *s;432 433	if (state->index >= state->aliases_len) {434		pr_err("Unexpected event %s/%s/\n", info->pmu->name, info->name);435		return 1;436	}437	s = &state->aliases[state->index];438	s->pmu = info->pmu;439#define COPY_STR(str) s->str = info->str ? strdup(info->str) : NULL440	COPY_STR(name);441	COPY_STR(alias);442	COPY_STR(scale_unit);443	COPY_STR(desc);444	COPY_STR(long_desc);445	COPY_STR(encoding_desc);446	COPY_STR(topic);447	COPY_STR(pmu_name);448	COPY_STR(event_type_desc);449#undef COPY_STR450	s->deprecated = info->deprecated;451	state->index++;452	return 0;453}454 455void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state)456{457	struct perf_pmu *pmu;458	int printed = 0;459	int len;460	struct sevent *aliases;461	struct events_callback_state state;462	bool skip_duplicate_pmus = print_cb->skip_duplicate_pmus(print_state);463	struct perf_pmu *(*scan_fn)(struct perf_pmu *);464 465	if (skip_duplicate_pmus)466		scan_fn = perf_pmus__scan_skip_duplicates;467	else468		scan_fn = perf_pmus__scan;469 470	pmu = NULL;471	len = 0;472	while ((pmu = scan_fn(pmu)) != NULL)473		len += perf_pmu__num_events(pmu);474 475	aliases = zalloc(sizeof(struct sevent) * len);476	if (!aliases) {477		pr_err("FATAL: not enough memory to print PMU events\n");478		return;479	}480	pmu = NULL;481	state = (struct events_callback_state) {482		.aliases = aliases,483		.aliases_len = len,484		.index = 0,485	};486	while ((pmu = scan_fn(pmu)) != NULL) {487		perf_pmu__for_each_event(pmu, skip_duplicate_pmus, &state,488					 perf_pmus__print_pmu_events__callback);489	}490	qsort(aliases, len, sizeof(struct sevent), cmp_sevent);491	for (int j = 0; j < len; j++) {492		/* Skip duplicates */493		if (j < len - 1 && pmu_alias_is_duplicate(&aliases[j], &aliases[j + 1]))494			goto free;495 496		print_cb->print_event(print_state,497				aliases[j].pmu_name,498				aliases[j].topic,499				aliases[j].name,500				aliases[j].alias,501				aliases[j].scale_unit,502				aliases[j].deprecated,503				aliases[j].event_type_desc,504				aliases[j].desc,505				aliases[j].long_desc,506				aliases[j].encoding_desc);507free:508		zfree(&aliases[j].name);509		zfree(&aliases[j].alias);510		zfree(&aliases[j].scale_unit);511		zfree(&aliases[j].desc);512		zfree(&aliases[j].long_desc);513		zfree(&aliases[j].encoding_desc);514		zfree(&aliases[j].topic);515		zfree(&aliases[j].pmu_name);516		zfree(&aliases[j].event_type_desc);517	}518	if (printed && pager_in_use())519		printf("\n");520 521	zfree(&aliases);522}523 524struct build_format_string_args {525	struct strbuf short_string;526	struct strbuf long_string;527	int num_formats;528};529 530static int build_format_string(void *state, const char *name, int config,531			       const unsigned long *bits)532{533	struct build_format_string_args *args = state;534	unsigned int num_bits;535	int ret1, ret2 = 0;536 537	(void)config;538	args->num_formats++;539	if (args->num_formats > 1) {540		strbuf_addch(&args->long_string, ',');541		if (args->num_formats < 4)542			strbuf_addch(&args->short_string, ',');543	}544	num_bits = bits ? bitmap_weight(bits, PERF_PMU_FORMAT_BITS) : 0;545	if (num_bits <= 1) {546		ret1 = strbuf_addf(&args->long_string, "%s", name);547		if (args->num_formats < 4)548			ret2 = strbuf_addf(&args->short_string, "%s", name);549	} else if (num_bits > 8) {550		ret1 = strbuf_addf(&args->long_string, "%s=0..0x%llx", name,551				   ULLONG_MAX >> (64 - num_bits));552		if (args->num_formats < 4) {553			ret2 = strbuf_addf(&args->short_string, "%s=0..0x%llx", name,554					   ULLONG_MAX >> (64 - num_bits));555		}556	} else {557		ret1 = strbuf_addf(&args->long_string, "%s=0..%llu", name,558				  ULLONG_MAX >> (64 - num_bits));559		if (args->num_formats < 4) {560			ret2 = strbuf_addf(&args->short_string, "%s=0..%llu", name,561					   ULLONG_MAX >> (64 - num_bits));562		}563	}564	return ret1 < 0 ? ret1 : (ret2 < 0 ? ret2 : 0);565}566 567void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, void *print_state)568{569	bool skip_duplicate_pmus = print_cb->skip_duplicate_pmus(print_state);570	struct perf_pmu *(*scan_fn)(struct perf_pmu *);571	struct perf_pmu *pmu = NULL;572 573	if (skip_duplicate_pmus)574		scan_fn = perf_pmus__scan_skip_duplicates;575	else576		scan_fn = perf_pmus__scan;577 578	while ((pmu = scan_fn(pmu)) != NULL) {579		struct build_format_string_args format_args = {580			.short_string = STRBUF_INIT,581			.long_string = STRBUF_INIT,582			.num_formats = 0,583		};584		int len = pmu_name_len_no_suffix(pmu->name);585		const char *desc = "(see 'man perf-list' or 'man perf-record' on how to encode it)";586 587		if (!pmu->is_core)588			desc = NULL;589 590		strbuf_addf(&format_args.short_string, "%.*s/", len, pmu->name);591		strbuf_addf(&format_args.long_string, "%.*s/", len, pmu->name);592		perf_pmu__for_each_format(pmu, &format_args, build_format_string);593 594		if (format_args.num_formats > 3)595			strbuf_addf(&format_args.short_string, ",.../modifier");596		else597			strbuf_addf(&format_args.short_string, "/modifier");598 599		strbuf_addf(&format_args.long_string, "/modifier");600		print_cb->print_event(print_state,601				/*topic=*/NULL,602				/*pmu_name=*/NULL,603				format_args.short_string.buf,604				/*event_alias=*/NULL,605				/*scale_unit=*/NULL,606				/*deprecated=*/false,607				"Raw event descriptor",608				desc,609				/*long_desc=*/NULL,610				format_args.long_string.buf);611 612		strbuf_release(&format_args.short_string);613		strbuf_release(&format_args.long_string);614	}615}616 617bool perf_pmus__have_event(const char *pname, const char *name)618{619	struct perf_pmu *pmu = perf_pmus__find(pname);620 621	return pmu && perf_pmu__have_event(pmu, name);622}623 624int perf_pmus__num_core_pmus(void)625{626	static int count;627 628	if (!count) {629		struct perf_pmu *pmu = NULL;630 631		while ((pmu = perf_pmus__scan_core(pmu)) != NULL)632			count++;633	}634	return count;635}636 637static bool __perf_pmus__supports_extended_type(void)638{639	struct perf_pmu *pmu = NULL;640 641	if (perf_pmus__num_core_pmus() <= 1)642		return false;643 644	while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {645		if (!is_event_supported(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES | ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT)))646			return false;647	}648 649	return true;650}651 652static bool perf_pmus__do_support_extended_type;653 654static void perf_pmus__init_supports_extended_type(void)655{656	perf_pmus__do_support_extended_type = __perf_pmus__supports_extended_type();657}658 659bool perf_pmus__supports_extended_type(void)660{661	static pthread_once_t extended_type_once = PTHREAD_ONCE_INIT;662 663	pthread_once(&extended_type_once, perf_pmus__init_supports_extended_type);664 665	return perf_pmus__do_support_extended_type;666}667 668char *perf_pmus__default_pmu_name(void)669{670	int fd;671	DIR *dir;672	struct dirent *dent;673	char *result = NULL;674 675	if (!list_empty(&core_pmus))676		return strdup(list_first_entry(&core_pmus, struct perf_pmu, list)->name);677 678	fd = perf_pmu__event_source_devices_fd();679	if (fd < 0)680		return strdup("cpu");681 682	dir = fdopendir(fd);683	if (!dir) {684		close(fd);685		return strdup("cpu");686	}687 688	while ((dent = readdir(dir))) {689		if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))690			continue;691		if (is_pmu_core(dent->d_name)) {692			result = strdup(dent->d_name);693			break;694		}695	}696 697	closedir(dir);698	return result ?: strdup("cpu");699}700 701struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)702{703	struct perf_pmu *pmu = evsel->pmu;704 705	if (!pmu) {706		pmu = perf_pmus__find_by_type(evsel->core.attr.type);707		((struct evsel *)evsel)->pmu = pmu;708	}709	return pmu;710}711 712struct perf_pmu *perf_pmus__find_core_pmu(void)713{714	return perf_pmus__scan_core(NULL);715}716 717struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name)718{719	/*720	 * Some PMU functions read from the sysfs mount point, so care is721	 * needed, hence passing the eager_load flag to load things like the722	 * format files.723	 */724	return perf_pmu__lookup(&other_pmus, test_sysfs_dirfd, name, /*eager_load=*/true);725}726 727struct perf_pmu *perf_pmus__fake_pmu(void)728{729	static struct perf_pmu fake = {730		.name = "fake",731		.type = PERF_PMU_TYPE_FAKE,732		.format = LIST_HEAD_INIT(fake.format),733	};734 735	return &fake;736}737