linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/2] perf tools: Add PMU alias support
@ 2021-08-11  2:48 Jin Yao
  2021-08-11  2:48 ` [PATCH v4 1/2] perf pmu: " Jin Yao
  2021-08-11  2:48 ` [PATCH v4 2/2] perf tests: Test for PMU alias Jin Yao
  0 siblings, 2 replies; 9+ messages in thread
From: Jin Yao @ 2021-08-11  2:48 UTC (permalink / raw)
  To: acme, jolsa, peterz, mingo, alexander.shishkin
  Cc: Linux-kernel, linux-perf-users, ak, kan.liang, yao.jin,
	rickyman7, Jin Yao

A perf uncore PMU may have two PMU names, a real name and an alias name.
With this patch set, the perf tool can monitor the PMU with either the
real name or the alias.

Use the real name,
 $ perf stat -e uncore_cha_2/event=1/ -x,
   4044879584,,uncore_cha_2/event=1/,2528059205,100.00,,

Use the alias,
 $ perf stat -e uncore_type_0_2/event=1/ -x,
   3659675336,,uncore_type_0_2/event=1/,2287306455,100.00,,

v4:
---
1. Fix memory leaks in pmu_lookup.
2. Rebase to perf/core.

v3:
---
1. Use fgets() to replace fscanf().
2. Resource cleanup.

v2:
---
Add test case to verify the real name and alias name having same effect.

Jin Yao (1):
  perf tests: Test for PMU alias

Kan Liang (1):
  perf pmu: Add PMU alias support

 tools/perf/arch/x86/util/pmu.c  | 129 +++++++++++++++++++++++++++++++-
 tools/perf/tests/parse-events.c |  92 +++++++++++++++++++++++
 tools/perf/util/parse-events.y  |   3 +-
 tools/perf/util/pmu.c           |  47 +++++++++---
 tools/perf/util/pmu.h           |   5 ++
 5 files changed, 264 insertions(+), 12 deletions(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v4 1/2] perf pmu: Add PMU alias support
  2021-08-11  2:48 [PATCH v4 0/2] perf tools: Add PMU alias support Jin Yao
@ 2021-08-11  2:48 ` Jin Yao
  2021-08-11 13:27   ` Andi Kleen
                     ` (2 more replies)
  2021-08-11  2:48 ` [PATCH v4 2/2] perf tests: Test for PMU alias Jin Yao
  1 sibling, 3 replies; 9+ messages in thread
From: Jin Yao @ 2021-08-11  2:48 UTC (permalink / raw)
  To: acme, jolsa, peterz, mingo, alexander.shishkin
  Cc: Linux-kernel, linux-perf-users, ak, kan.liang, yao.jin,
	rickyman7, Kan Liang, Jin Yao

From: Kan Liang <kan.liang@linux.intel.com>

A perf uncore PMU may have two PMU names, a real name and an alias. The
alias is exported at /sys/bus/event_source/devices/uncore_*/alias.
The perf tool should support the alias as well.

Add alias_name in the struct perf_pmu to store the alias. For the PMU
which doesn't have an alias. It's NULL.

Introduce two X86 specific functions to retrieve the real name and the
alias separately.

Only go through the sysfs to retrieve the mapping between the real name
and the alias once. The result is cached in a list, uncore_pmu_list.

Nothing changed for the other ARCHs.

With the patch, the perf tool can monitor the PMU with either the real
name or the alias.

Use the real name,
 $ perf stat -e uncore_cha_2/event=1/ -x,
   4044879584,,uncore_cha_2/event=1/,2528059205,100.00,,

Use the alias,
 $ perf stat -e uncore_type_0_2/event=1/ -x,
   3659675336,,uncore_type_0_2/event=1/,2287306455,100.00,,

Co-developed-by: Jin Yao <yao.jin@linux.intel.com>
Signed-off-by: Jin Yao <yao.jin@linux.intel.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
v4:
 - Fix memory leaks in pmu_lookup.
 - Rebase to perf/core.

v3:
 - Use fgets to read alias string from sysfs.
 - Resource cleanup.

v2:
 - No change.

 tools/perf/arch/x86/util/pmu.c | 129 ++++++++++++++++++++++++++++++++-
 tools/perf/util/parse-events.y |   3 +-
 tools/perf/util/pmu.c          |  47 +++++++++---
 tools/perf/util/pmu.h          |   5 ++
 4 files changed, 172 insertions(+), 12 deletions(-)

diff --git a/tools/perf/arch/x86/util/pmu.c b/tools/perf/arch/x86/util/pmu.c
index d48d608517fd..9abba67cc62e 100644
--- a/tools/perf/arch/x86/util/pmu.c
+++ b/tools/perf/arch/x86/util/pmu.c
@@ -1,12 +1,29 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <string.h>
-
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
 #include <linux/stddef.h>
 #include <linux/perf_event.h>
+#include <linux/zalloc.h>
+#include <api/fs/fs.h>
+#include <errno.h>
 
 #include "../../../util/intel-pt.h"
 #include "../../../util/intel-bts.h"
 #include "../../../util/pmu.h"
+#include "../../../util/fncache.h"
+
+#define TEMPLATE_ALIAS	"%s/bus/event_source/devices/%s/alias"
+
+struct perf_pmu_alias_name {
+	char *name;
+	char *alias;
+	struct list_head list;
+};
+
+static LIST_HEAD(pmu_alias_name_list);
 
 struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
 {
@@ -18,3 +35,113 @@ struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __mayb
 #endif
 	return NULL;
 }
+
+static int setup_pmu_alias_list(void)
+{
+	char path[PATH_MAX];
+	DIR *dir;
+	struct dirent *dent;
+	const char *sysfs = sysfs__mountpoint();
+	struct perf_pmu_alias_name *pmu;
+	char buf[MAX_PMU_NAME_LEN];
+	FILE *file;
+	int ret = 0;
+
+	if (!sysfs)
+		return -1;
+
+	snprintf(path, PATH_MAX,
+		 "%s" EVENT_SOURCE_DEVICE_PATH, sysfs);
+
+	dir = opendir(path);
+	if (!dir)
+		return -1;
+
+	while ((dent = readdir(dir))) {
+		if (!strcmp(dent->d_name, ".") ||
+		    !strcmp(dent->d_name, ".."))
+			continue;
+
+		snprintf(path, PATH_MAX,
+			 TEMPLATE_ALIAS, sysfs, dent->d_name);
+
+		if (!file_available(path))
+			continue;
+
+		file = fopen(path, "r");
+		if (!file)
+			continue;
+
+		if (!fgets(buf, sizeof(buf), file)) {
+			fclose(file);
+			continue;
+		}
+
+		fclose(file);
+
+		pmu = zalloc(sizeof(*pmu));
+		if (!pmu) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		/* Remove the last '\n' */
+		buf[strlen(buf) - 1] = 0;
+
+		pmu->alias = strdup(buf);
+		if (!pmu->alias)
+			goto mem_err;
+
+		pmu->name = strdup(dent->d_name);
+		if (!pmu->name)
+			goto mem_err;
+
+		list_add_tail(&pmu->list, &pmu_alias_name_list);
+		continue;
+mem_err:
+		ret = -ENOMEM;
+		free(pmu->alias);
+		free(pmu->name);
+		free(pmu);
+		break;
+	}
+
+	closedir(dir);
+	return ret;
+}
+
+static char *__pmu_find_real_name(const char *name)
+{
+	struct perf_pmu_alias_name *pmu;
+
+	list_for_each_entry(pmu, &pmu_alias_name_list, list) {
+		if (!strcmp(name, pmu->alias))
+			return strdup(pmu->name);
+	}
+
+	return strdup(name);
+}
+
+char *pmu_find_real_name(const char *name)
+{
+	static bool cached_list;
+
+	if (cached_list)
+		return __pmu_find_real_name(name);
+
+	setup_pmu_alias_list();
+	cached_list = true;
+
+	return __pmu_find_real_name(name);
+}
+
+char *pmu_find_alias_name(const char *name)
+{
+	struct perf_pmu_alias_name *pmu;
+
+	list_for_each_entry(pmu, &pmu_alias_name_list, list) {
+		if (!strcmp(name, pmu->name))
+			return strdup(pmu->alias);
+	}
+	return NULL;
+}
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 9321bd0e2f76..d94e48e1ff9b 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -316,7 +316,8 @@ event_pmu_name opt_pmu_config
 			if (!strncmp(name, "uncore_", 7) &&
 			    strncmp($1, "uncore_", 7))
 				name += 7;
-			if (!perf_pmu__match(pattern, name, $1)) {
+			if (!perf_pmu__match(pattern, name, $1) ||
+			    !perf_pmu__match(pattern, pmu->alias_name, $1)) {
 				if (parse_events_copy_term_list(orig_terms, &terms))
 					CLEANUP_YYABORT;
 				if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index fc683bc41715..796a4be752f4 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -946,6 +946,18 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
 	return NULL;
 }
 
+char * __weak
+pmu_find_real_name(const char *name)
+{
+	return strdup(name);
+}
+
+char * __weak
+pmu_find_alias_name(const char *name __maybe_unused)
+{
+	return NULL;
+}
+
 static int pmu_max_precise(const char *name)
 {
 	char path[PATH_MAX];
@@ -959,19 +971,25 @@ static int pmu_max_precise(const char *name)
 	return max_precise;
 }
 
-static struct perf_pmu *pmu_lookup(const char *name)
+static struct perf_pmu *pmu_lookup(const char *lookup_name)
 {
-	struct perf_pmu *pmu;
+	struct perf_pmu *pmu = NULL;
 	LIST_HEAD(format);
 	LIST_HEAD(aliases);
 	__u32 type;
-	bool is_hybrid = perf_pmu__hybrid_mounted(name);
+	bool is_hybrid;
+	char *name = pmu_find_real_name(lookup_name);
+
+	if (!name)
+		return NULL;
+
+	is_hybrid = perf_pmu__hybrid_mounted(name);
 
 	/*
 	 * Check pmu name for hybrid and the pmu may be invalid in sysfs
 	 */
 	if (!strncmp(name, "cpu_", 4) && !is_hybrid)
-		return NULL;
+		goto out;
 
 	/*
 	 * The pmu data we store & need consists of the pmu
@@ -979,23 +997,24 @@ static struct perf_pmu *pmu_lookup(const char *name)
 	 * now.
 	 */
 	if (pmu_format(name, &format))
-		return NULL;
+		goto out;
 
 	/*
 	 * Check the type first to avoid unnecessary work.
 	 */
 	if (pmu_type(name, &type))
-		return NULL;
+		goto out;
 
 	if (pmu_aliases(name, &aliases))
-		return NULL;
+		goto out;
 
 	pmu = zalloc(sizeof(*pmu));
 	if (!pmu)
-		return NULL;
+		goto out;
 
 	pmu->cpus = pmu_cpumask(name);
-	pmu->name = strdup(name);
+	pmu->name = name;
+	pmu->alias_name = pmu_find_alias_name(name);
 	pmu->type = type;
 	pmu->is_uncore = pmu_is_uncore(name);
 	if (pmu->is_uncore)
@@ -1017,6 +1036,10 @@ static struct perf_pmu *pmu_lookup(const char *name)
 
 	pmu->default_config = perf_pmu__get_default_config(pmu);
 
+out:
+	if (!pmu)
+		free(name);
+
 	return pmu;
 }
 
@@ -1025,7 +1048,8 @@ static struct perf_pmu *pmu_find(const char *name)
 	struct perf_pmu *pmu;
 
 	list_for_each_entry(pmu, &pmus, list)
-		if (!strcmp(pmu->name, name))
+		if (!strcmp(pmu->name, name) ||
+		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))
 			return pmu;
 
 	return NULL;
@@ -1920,6 +1944,9 @@ bool perf_pmu__has_hybrid(void)
 
 int perf_pmu__match(char *pattern, char *name, char *tok)
 {
+	if (!name)
+		return -1;
+
 	if (fnmatch(pattern, name, 0))
 		return -1;
 
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 926da483a141..f6ca9f6a06ef 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -21,6 +21,7 @@ enum {
 #define PERF_PMU_FORMAT_BITS 64
 #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
 #define CPUS_TEMPLATE_CPU	"%s/bus/event_source/devices/%s/cpus"
+#define MAX_PMU_NAME_LEN 128
 
 struct perf_event_attr;
 
@@ -32,6 +33,7 @@ struct perf_pmu_caps {
 
 struct perf_pmu {
 	char *name;
+	char *alias_name;	/* PMU alias name */
 	char *id;
 	__u32 type;
 	bool selectable;
@@ -135,4 +137,7 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
 bool perf_pmu__has_hybrid(void);
 int perf_pmu__match(char *pattern, char *name, char *tok);
 
+char *pmu_find_real_name(const char *name);
+char *pmu_find_alias_name(const char *name);
+
 #endif /* __PMU_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 2/2] perf tests: Test for PMU alias
  2021-08-11  2:48 [PATCH v4 0/2] perf tools: Add PMU alias support Jin Yao
  2021-08-11  2:48 ` [PATCH v4 1/2] perf pmu: " Jin Yao
@ 2021-08-11  2:48 ` Jin Yao
  1 sibling, 0 replies; 9+ messages in thread
From: Jin Yao @ 2021-08-11  2:48 UTC (permalink / raw)
  To: acme, jolsa, peterz, mingo, alexander.shishkin
  Cc: Linux-kernel, linux-perf-users, ak, kan.liang, yao.jin,
	rickyman7, Jin Yao

A perf uncore PMU may have two PMU names, a real name and an alias
name. Add one test case to verify the real name and alias name having
the same effect.

Iterate the sysfs to get one event which has an alias and create
evlist by adding two evsels. Evsel1 is created by event and evsel2
is created by alias.

Test asserts:
evsel1->core.attr.type == evsel2->core.attr.type
evsel1->core.attr.config == evsel2->core.attr.config

Signed-off-by: Jin Yao <yao.jin@linux.intel.com>
---
v4:
 - Rebase to perf/core.

v3:
 - Use fgets to read alias string from sysfs.
 - Resource cleanup.

v2:
 - New in v2.

 tools/perf/tests/parse-events.c | 92 +++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 8d4866739255..fd3556cc9ad4 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -9,6 +9,7 @@
 #include "pmu-hybrid.h"
 #include <dirent.h>
 #include <errno.h>
+#include "fncache.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -2194,9 +2195,91 @@ static int test_pmu_events(void)
 	return ret;
 }
 
+static bool test_alias(char **event, char **alias)
+{
+	char path[PATH_MAX];
+	DIR *dir;
+	struct dirent *dent;
+	const char *sysfs = sysfs__mountpoint();
+	char buf[128];
+	FILE *file;
+
+	if (!sysfs)
+		return false;
+
+	snprintf(path, PATH_MAX, "%s/bus/event_source/devices/", sysfs);
+	dir = opendir(path);
+	if (!dir)
+		return false;
+
+	while ((dent = readdir(dir))) {
+		if (!strcmp(dent->d_name, ".") ||
+		    !strcmp(dent->d_name, ".."))
+			continue;
+
+		snprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s/alias",
+			 sysfs, dent->d_name);
+
+		if (!file_available(path))
+			continue;
+
+		file = fopen(path, "r");
+		if (!file)
+			continue;
+
+		if (!fgets(buf, sizeof(buf), file)) {
+			fclose(file);
+			continue;
+		}
+
+		/* Remove the last '\n' */
+		buf[strlen(buf) - 1] = 0;
+
+		fclose(file);
+		*event = strdup(dent->d_name);
+		*alias = strdup(buf);
+		closedir(dir);
+
+		if (*event == NULL || *alias == NULL) {
+			free(*event);
+			free(*alias);
+			return false;
+		}
+
+		return true;
+	}
+
+	closedir(dir);
+	return false;
+}
+
+static int test__checkevent_pmu_events_alias(struct evlist *evlist)
+{
+	struct evsel *evsel1 = evlist__first(evlist);
+	struct evsel *evsel2 = evlist__last(evlist);
+
+	TEST_ASSERT_VAL("wrong type", evsel1->core.attr.type == evsel2->core.attr.type);
+	TEST_ASSERT_VAL("wrong config", evsel1->core.attr.config == evsel2->core.attr.config);
+	return 0;
+}
+
+static int test_pmu_events_alias(char *event, char *alias)
+{
+	struct evlist_test e = { .id = 0, };
+	char name[2 * NAME_MAX + 20];
+
+	snprintf(name, sizeof(name), "%s/event=1/,%s/event=1/",
+		 event, alias);
+
+	e.name  = name;
+	e.check = test__checkevent_pmu_events_alias;
+	return test_event(&e);
+}
+
 int test__parse_events(struct test *test __maybe_unused, int subtest __maybe_unused)
 {
 	int ret1, ret2 = 0;
+	char *event, *alias;
 
 #define TEST_EVENTS(tests)				\
 do {							\
@@ -2221,6 +2304,15 @@ do {							\
 			return ret;
 	}
 
+	if (test_alias(&event, &alias)) {
+		int ret = test_pmu_events_alias(event, alias);
+
+		free(event);
+		free(alias);
+		if (ret)
+			return ret;
+	}
+
 	ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms));
 	if (!ret2)
 		ret2 = ret1;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/2] perf pmu: Add PMU alias support
  2021-08-11  2:48 ` [PATCH v4 1/2] perf pmu: " Jin Yao
@ 2021-08-11 13:27   ` Andi Kleen
  2021-08-12  4:39     ` Jin, Yao
  2021-08-11 19:23   ` Jiri Olsa
  2021-08-12 12:14   ` John Garry
  2 siblings, 1 reply; 9+ messages in thread
From: Andi Kleen @ 2021-08-11 13:27 UTC (permalink / raw)
  To: Jin Yao, acme, jolsa, peterz, mingo, alexander.shishkin
  Cc: Linux-kernel, linux-perf-users, kan.liang, yao.jin, rickyman7, Kan Liang


> +
> +	dir = opendir(path);
> +	if (!dir)
> +		return -1;


Could we cache/check the opendir result through file_available too?


The rest looks good


Reviewed-by: Andi Kleen <ak@linux.intel.com>

-Andi


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/2] perf pmu: Add PMU alias support
  2021-08-11  2:48 ` [PATCH v4 1/2] perf pmu: " Jin Yao
  2021-08-11 13:27   ` Andi Kleen
@ 2021-08-11 19:23   ` Jiri Olsa
  2021-08-12  4:32     ` Jin, Yao
  2021-08-12 12:14   ` John Garry
  2 siblings, 1 reply; 9+ messages in thread
From: Jiri Olsa @ 2021-08-11 19:23 UTC (permalink / raw)
  To: Jin Yao
  Cc: acme, jolsa, peterz, mingo, alexander.shishkin, Linux-kernel,
	linux-perf-users, ak, kan.liang, yao.jin, rickyman7, Kan Liang

On Wed, Aug 11, 2021 at 10:48:26AM +0800, Jin Yao wrote:

SNIP

>  				if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index fc683bc41715..796a4be752f4 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -946,6 +946,18 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
>  	return NULL;
>  }
>  
> +char * __weak
> +pmu_find_real_name(const char *name)
> +{
> +	return strdup(name);
> +}

hm, why does this need to return already strdup? it forces you
to add all those goto below.. could just return name and keep
the 'pmu->name = strdup(name);' below?

that should make the change simpler

jirka

> +
> +char * __weak
> +pmu_find_alias_name(const char *name __maybe_unused)
> +{
> +	return NULL;
> +}
> +
>  static int pmu_max_precise(const char *name)
>  {
>  	char path[PATH_MAX];
> @@ -959,19 +971,25 @@ static int pmu_max_precise(const char *name)
>  	return max_precise;
>  }
>  
> -static struct perf_pmu *pmu_lookup(const char *name)
> +static struct perf_pmu *pmu_lookup(const char *lookup_name)
>  {
> -	struct perf_pmu *pmu;
> +	struct perf_pmu *pmu = NULL;
>  	LIST_HEAD(format);
>  	LIST_HEAD(aliases);
>  	__u32 type;
> -	bool is_hybrid = perf_pmu__hybrid_mounted(name);
> +	bool is_hybrid;
> +	char *name = pmu_find_real_name(lookup_name);
> +
> +	if (!name)
> +		return NULL;
> +
> +	is_hybrid = perf_pmu__hybrid_mounted(name);
>  
>  	/*
>  	 * Check pmu name for hybrid and the pmu may be invalid in sysfs
>  	 */
>  	if (!strncmp(name, "cpu_", 4) && !is_hybrid)
> -		return NULL;
> +		goto out;
>  
>  	/*
>  	 * The pmu data we store & need consists of the pmu
> @@ -979,23 +997,24 @@ static struct perf_pmu *pmu_lookup(const char *name)
>  	 * now.
>  	 */
>  	if (pmu_format(name, &format))
> -		return NULL;
> +		goto out;
>  
>  	/*
>  	 * Check the type first to avoid unnecessary work.
>  	 */
>  	if (pmu_type(name, &type))
> -		return NULL;
> +		goto out;
>  
>  	if (pmu_aliases(name, &aliases))
> -		return NULL;
> +		goto out;
>  
>  	pmu = zalloc(sizeof(*pmu));
>  	if (!pmu)
> -		return NULL;
> +		goto out;
>  
>  	pmu->cpus = pmu_cpumask(name);
> -	pmu->name = strdup(name);
> +	pmu->name = name;
> +	pmu->alias_name = pmu_find_alias_name(name);
>  	pmu->type = type;
>  	pmu->is_uncore = pmu_is_uncore(name);
>  	if (pmu->is_uncore)
> @@ -1017,6 +1036,10 @@ static struct perf_pmu *pmu_lookup(const char *name)
>  
>  	pmu->default_config = perf_pmu__get_default_config(pmu);
>  
> +out:
> +	if (!pmu)
> +		free(name);
> +
>  	return pmu;
>  }
>  
> @@ -1025,7 +1048,8 @@ static struct perf_pmu *pmu_find(const char *name)
>  	struct perf_pmu *pmu;
>  
>  	list_for_each_entry(pmu, &pmus, list)
> -		if (!strcmp(pmu->name, name))
> +		if (!strcmp(pmu->name, name) ||
> +		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))
>  			return pmu;
>  
>  	return NULL;
> @@ -1920,6 +1944,9 @@ bool perf_pmu__has_hybrid(void)
>  
>  int perf_pmu__match(char *pattern, char *name, char *tok)
>  {
> +	if (!name)
> +		return -1;
> +
>  	if (fnmatch(pattern, name, 0))
>  		return -1;
>  
> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> index 926da483a141..f6ca9f6a06ef 100644
> --- a/tools/perf/util/pmu.h
> +++ b/tools/perf/util/pmu.h
> @@ -21,6 +21,7 @@ enum {
>  #define PERF_PMU_FORMAT_BITS 64
>  #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
>  #define CPUS_TEMPLATE_CPU	"%s/bus/event_source/devices/%s/cpus"
> +#define MAX_PMU_NAME_LEN 128
>  
>  struct perf_event_attr;
>  
> @@ -32,6 +33,7 @@ struct perf_pmu_caps {
>  
>  struct perf_pmu {
>  	char *name;
> +	char *alias_name;	/* PMU alias name */
>  	char *id;
>  	__u32 type;
>  	bool selectable;
> @@ -135,4 +137,7 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
>  bool perf_pmu__has_hybrid(void);
>  int perf_pmu__match(char *pattern, char *name, char *tok);
>  
> +char *pmu_find_real_name(const char *name);
> +char *pmu_find_alias_name(const char *name);
> +
>  #endif /* __PMU_H */
> -- 
> 2.17.1
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/2] perf pmu: Add PMU alias support
  2021-08-11 19:23   ` Jiri Olsa
@ 2021-08-12  4:32     ` Jin, Yao
  0 siblings, 0 replies; 9+ messages in thread
From: Jin, Yao @ 2021-08-12  4:32 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: acme, jolsa, peterz, mingo, alexander.shishkin, Linux-kernel,
	linux-perf-users, ak, kan.liang, yao.jin, rickyman7, Kan Liang

Hi Jiri,

On 8/12/2021 3:23 AM, Jiri Olsa wrote:
> On Wed, Aug 11, 2021 at 10:48:26AM +0800, Jin Yao wrote:
> 
> SNIP
> 
>>   				if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
>> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
>> index fc683bc41715..796a4be752f4 100644
>> --- a/tools/perf/util/pmu.c
>> +++ b/tools/perf/util/pmu.c
>> @@ -946,6 +946,18 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
>>   	return NULL;
>>   }
>>   
>> +char * __weak
>> +pmu_find_real_name(const char *name)
>> +{
>> +	return strdup(name);
>> +}
> hm, why does this need to return already strdup? it forces you
> to add all those goto below.. could just return name and keep
> the 'pmu->name = strdup(name);' below?
> 
> that should make the change simpler
> 
> jirka
> 

In x86 specific __pmu_find_real_name,

static char *__pmu_find_real_name(const char *name)
{
	struct perf_pmu_alias_name *pmu;

	list_for_each_entry(pmu, &pmu_alias_name_list, list) {
		if (!strcmp(name, pmu->alias))
			return strdup(pmu->name);
	}

	return strdup(name);
}

We have returned with strdup(name).

OK, I will change the code to:

static char *__pmu_find_real_name(const char *name)
{
	struct perf_pmu_alias_name *pmu;

	list_for_each_entry(pmu, &pmu_alias_name_list, list) {
		if (!strcmp(name, pmu->alias))
			return pmu->name;
	}

	return name;
}

And keep 'pmu->name = strdup(name);' in pmu_lookup().

Thanks
Jin Yao

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/2] perf pmu: Add PMU alias support
  2021-08-11 13:27   ` Andi Kleen
@ 2021-08-12  4:39     ` Jin, Yao
  0 siblings, 0 replies; 9+ messages in thread
From: Jin, Yao @ 2021-08-12  4:39 UTC (permalink / raw)
  To: Andi Kleen, acme, jolsa, peterz, mingo, alexander.shishkin
  Cc: Linux-kernel, linux-perf-users, kan.liang, yao.jin, rickyman7, Kan Liang



On 8/11/2021 9:27 PM, Andi Kleen wrote:
> 
>> +
>> +    dir = opendir(path);
>> +    if (!dir)
>> +        return -1;
> 
> 
> Could we cache/check the opendir result through file_available too?
> 
> 
> The rest looks good
> 
> 
> Reviewed-by: Andi Kleen <ak@linux.intel.com>
> 
> -Andi

Thanks Andi! Let me check how to do it.

Thanks
Jin Yao

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/2] perf pmu: Add PMU alias support
  2021-08-11  2:48 ` [PATCH v4 1/2] perf pmu: " Jin Yao
  2021-08-11 13:27   ` Andi Kleen
  2021-08-11 19:23   ` Jiri Olsa
@ 2021-08-12 12:14   ` John Garry
  2021-08-13  1:59     ` Jin, Yao
  2 siblings, 1 reply; 9+ messages in thread
From: John Garry @ 2021-08-12 12:14 UTC (permalink / raw)
  To: Jin Yao, acme, jolsa, peterz, mingo, alexander.shishkin
  Cc: Linux-kernel, linux-perf-users, ak, kan.liang, yao.jin,
	rickyman7, Kan Liang

perf pmu: Add x86 PMU alias support

> +char *pmu_find_real_name(const char *name)
> +{
> +	static bool cached_list;
> +
> +	if (cached_list)
> +		return __pmu_find_real_name(name);
> +
> +	setup_pmu_alias_list();
> +	cached_list = true;
> +
> +	return __pmu_find_real_name(name);
> +}
> +
> +char *pmu_find_alias_name(const char *name)
> +{
> +	struct perf_pmu_alias_name *pmu;
> +
> +	list_for_each_entry(pmu, &pmu_alias_name_list, list) {
> +		if (!strcmp(name, pmu->name))
> +			return strdup(pmu->alias);

I would not expect a function which does a "find" to duplicate the name.

Same goes for all the other places which does similar.

> +	}
> +	return NULL;
> +}
> diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
> index 9321bd0e2f76..d94e48e1ff9b 100644
> --- a/tools/perf/util/parse-events.y
> +++ b/tools/perf/util/parse-events.y
> @@ -316,7 +316,8 @@ event_pmu_name opt_pmu_config
>   			if (!strncmp(name, "uncore_", 7) &&
>   			    strncmp($1, "uncore_", 7))
>   				name += 7;
> -			if (!perf_pmu__match(pattern, name, $1)) {
> +			if (!perf_pmu__match(pattern, name, $1) ||
> +			    !perf_pmu__match(pattern, pmu->alias_name, $1)) {
>   				if (parse_events_copy_term_list(orig_terms, &terms))
>   					CLEANUP_YYABORT;
>   				if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index fc683bc41715..796a4be752f4 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -946,6 +946,18 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
>   	return NULL;
>   }
>   
> +char * __weak
> +pmu_find_real_name(const char *name)
> +{
> +	return strdup(name);
> +}

That's not finding anything.

> +
> +char * __weak
> +pmu_find_alias_name(const char *name __maybe_unused)
> +{
> +	return NULL;
 > +}
> +
>   static int pmu_max_precise(const char *name)
>   {
>   	char path[PATH_MAX];
> @@ -959,19 +971,25 @@ static int pmu_max_precise(const char *name)
>   	return max_precise;
>   }
>   
> -static struct perf_pmu *pmu_lookup(const char *name)
> +static struct perf_pmu *pmu_lookup(const char *lookup_name)
>   {
> -	struct perf_pmu *pmu;
> +	struct perf_pmu *pmu = NULL;
>   	LIST_HEAD(format);
>   	LIST_HEAD(aliases);
>   	__u32 type;
> -	bool is_hybrid = perf_pmu__hybrid_mounted(name);
> +	bool is_hybrid;
> +	char *name = pmu_find_real_name(lookup_name);
> +
> +	if (!name)
> +		return NULL;
> +
> +	is_hybrid = perf_pmu__hybrid_mounted(name);
>   
>   	/*
>   	 * Check pmu name for hybrid and the pmu may be invalid in sysfs
>   	 */
>   	if (!strncmp(name, "cpu_", 4) && !is_hybrid)
> -		return NULL;
> +		goto out;
>   
>   	/*
>   	 * The pmu data we store & need consists of the pmu
> @@ -979,23 +997,24 @@ static struct perf_pmu *pmu_lookup(const char *name)
>   	 * now.
>   	 */
>   	if (pmu_format(name, &format))
> -		return NULL;
> +		goto out;
>   
>   	/*
>   	 * Check the type first to avoid unnecessary work.
>   	 */
>   	if (pmu_type(name, &type))
> -		return NULL;
> +		goto out;
>   
>   	if (pmu_aliases(name, &aliases))
> -		return NULL;
> +		goto out;
>   
>   	pmu = zalloc(sizeof(*pmu));
>   	if (!pmu)
> -		return NULL;
> +		goto out;
>   
>   	pmu->cpus = pmu_cpumask(name);
> -	pmu->name = strdup(name);
> +	pmu->name = name;
> +	pmu->alias_name = pmu_find_alias_name(name);
>   	pmu->type = type;
>   	pmu->is_uncore = pmu_is_uncore(name);
>   	if (pmu->is_uncore)
> @@ -1017,6 +1036,10 @@ static struct perf_pmu *pmu_lookup(const char *name)
>   
>   	pmu->default_config = perf_pmu__get_default_config(pmu);
>   
> +out:
> +	if (!pmu)
> +		free(name);

I don't understand this. There are lots of places this function can fail 
, but we don't free memories allocated previously - why just free this one?

> +
>   	return pmu;
>   }
>   
> @@ -1025,7 +1048,8 @@ static struct perf_pmu *pmu_find(const char *name)
>   	struct perf_pmu *pmu;
>   
>   	list_for_each_entry(pmu, &pmus, list)
> -		if (!strcmp(pmu->name, name))
> +		if (!strcmp(pmu->name, name) ||
> +		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))
>   			return pmu;

I'd be inclined to use {} for the list_for_each_entry() call

>   
>   	return NULL;
> @@ -1920,6 +1944,9 @@ bool perf_pmu__has_hybrid(void)
>   
>   int perf_pmu__match(char *pattern, char *name, char *tok)
>   {
> +	if (!name)
> +		return -1;
> +
>   	if (fnmatch(pattern, name, 0))
>   		return -1;
>   
> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
> index 926da483a141..f6ca9f6a06ef 100644
> --- a/tools/perf/util/pmu.h
> +++ b/tools/perf/util/pmu.h
> @@ -21,6 +21,7 @@ enum {
>   #define PERF_PMU_FORMAT_BITS 64
>   #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
>   #define CPUS_TEMPLATE_CPU	"%s/bus/event_source/devices/%s/cpus"
> +#define MAX_PMU_NAME_LEN 128
>   
>   struct perf_event_attr;
>   
> @@ -32,6 +33,7 @@ struct perf_pmu_caps {
>   
>   struct perf_pmu {
>   	char *name;
> +	char *alias_name;	/* PMU alias name */

useless comment

>   	char *id;
>   	__u32 type;
>   	bool selectable;
> @@ -135,4 +137,7 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
>   bool perf_pmu__has_hybrid(void);
>   int perf_pmu__match(char *pattern, char *name, char *tok);
>   
> +char *pmu_find_real_name(const char *name);
> +char *pmu_find_alias_name(const char *name);
> +
>   #endif /* __PMU_H */
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/2] perf pmu: Add PMU alias support
  2021-08-12 12:14   ` John Garry
@ 2021-08-13  1:59     ` Jin, Yao
  0 siblings, 0 replies; 9+ messages in thread
From: Jin, Yao @ 2021-08-13  1:59 UTC (permalink / raw)
  To: John Garry, acme, jolsa, peterz, mingo, alexander.shishkin
  Cc: Linux-kernel, linux-perf-users, ak, kan.liang, yao.jin,
	rickyman7, Kan Liang

Hi John,

On 8/12/2021 8:14 PM, John Garry wrote:
> perf pmu: Add x86 PMU alias support
> 
>> +char *pmu_find_real_name(const char *name)
>> +{
>> +    static bool cached_list;
>> +
>> +    if (cached_list)
>> +        return __pmu_find_real_name(name);
>> +
>> +    setup_pmu_alias_list();
>> +    cached_list = true;
>> +
>> +    return __pmu_find_real_name(name);
>> +}
>> +
>> +char *pmu_find_alias_name(const char *name)
>> +{
>> +    struct perf_pmu_alias_name *pmu;
>> +
>> +    list_for_each_entry(pmu, &pmu_alias_name_list, list) {
>> +        if (!strcmp(name, pmu->name))
>> +            return strdup(pmu->alias);
> 
> I would not expect a function which does a "find" to duplicate the name.
> 
> Same goes for all the other places which does similar.
> 

Yes, you are right. Jiri has the similar comment for the name duplicating here. We will still keep 
the 'pmu->name = strdup(name);' in pmu_lookup() in v5.

>> +    }
>> +    return NULL;
>> +}
>> diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
>> index 9321bd0e2f76..d94e48e1ff9b 100644
>> --- a/tools/perf/util/parse-events.y
>> +++ b/tools/perf/util/parse-events.y
>> @@ -316,7 +316,8 @@ event_pmu_name opt_pmu_config
>>               if (!strncmp(name, "uncore_", 7) &&
>>                   strncmp($1, "uncore_", 7))
>>                   name += 7;
>> -            if (!perf_pmu__match(pattern, name, $1)) {
>> +            if (!perf_pmu__match(pattern, name, $1) ||
>> +                !perf_pmu__match(pattern, pmu->alias_name, $1)) {
>>                   if (parse_events_copy_term_list(orig_terms, &terms))
>>                       CLEANUP_YYABORT;
>>                   if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
>> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
>> index fc683bc41715..796a4be752f4 100644
>> --- a/tools/perf/util/pmu.c
>> +++ b/tools/perf/util/pmu.c
>> @@ -946,6 +946,18 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
>>       return NULL;
>>   }
>> +char * __weak
>> +pmu_find_real_name(const char *name)
>> +{
>> +    return strdup(name);
>> +}
> 
> That's not finding anything.
> 

Will just return name in non-x86 implementation.

pmu_find_real_name(const char *name)
{
	return name;
}

>> +
>> +char * __weak
>> +pmu_find_alias_name(const char *name __maybe_unused)
>> +{
>> +    return NULL;
>  > +}
>> +
>>   static int pmu_max_precise(const char *name)
>>   {
>>       char path[PATH_MAX];
>> @@ -959,19 +971,25 @@ static int pmu_max_precise(const char *name)
>>       return max_precise;
>>   }
>> -static struct perf_pmu *pmu_lookup(const char *name)
>> +static struct perf_pmu *pmu_lookup(const char *lookup_name)
>>   {
>> -    struct perf_pmu *pmu;
>> +    struct perf_pmu *pmu = NULL;
>>       LIST_HEAD(format);
>>       LIST_HEAD(aliases);
>>       __u32 type;
>> -    bool is_hybrid = perf_pmu__hybrid_mounted(name);
>> +    bool is_hybrid;
>> +    char *name = pmu_find_real_name(lookup_name);
>> +
>> +    if (!name)
>> +        return NULL;
>> +
>> +    is_hybrid = perf_pmu__hybrid_mounted(name);
>>       /*
>>        * Check pmu name for hybrid and the pmu may be invalid in sysfs
>>        */
>>       if (!strncmp(name, "cpu_", 4) && !is_hybrid)
>> -        return NULL;
>> +        goto out;
>>       /*
>>        * The pmu data we store & need consists of the pmu
>> @@ -979,23 +997,24 @@ static struct perf_pmu *pmu_lookup(const char *name)
>>        * now.
>>        */
>>       if (pmu_format(name, &format))
>> -        return NULL;
>> +        goto out;
>>       /*
>>        * Check the type first to avoid unnecessary work.
>>        */
>>       if (pmu_type(name, &type))
>> -        return NULL;
>> +        goto out;
>>       if (pmu_aliases(name, &aliases))
>> -        return NULL;
>> +        goto out;
>>       pmu = zalloc(sizeof(*pmu));
>>       if (!pmu)
>> -        return NULL;
>> +        goto out;
>>       pmu->cpus = pmu_cpumask(name);
>> -    pmu->name = strdup(name);
>> +    pmu->name = name;
>> +    pmu->alias_name = pmu_find_alias_name(name);
>>       pmu->type = type;
>>       pmu->is_uncore = pmu_is_uncore(name);
>>       if (pmu->is_uncore)
>> @@ -1017,6 +1036,10 @@ static struct perf_pmu *pmu_lookup(const char *name)
>>       pmu->default_config = perf_pmu__get_default_config(pmu);
>> +out:
>> +    if (!pmu)
>> +        free(name);
> 
> I don't understand this. There are lots of places this function can fail , but we don't free 
> memories allocated previously - why just free this one?
>

That's because it's only introduced by my patch (duplicating name). :)

But if we keep 'pmu->name = strdup(name)' in pmu_lookup(), I'm OK to keep original code/behavior.

>> +
>>       return pmu;
>>   }
>> @@ -1025,7 +1048,8 @@ static struct perf_pmu *pmu_find(const char *name)
>>       struct perf_pmu *pmu;
>>       list_for_each_entry(pmu, &pmus, list)
>> -        if (!strcmp(pmu->name, name))
>> +        if (!strcmp(pmu->name, name) ||
>> +            (pmu->alias_name && !strcmp(pmu->alias_name, name)))
>>               return pmu;
> 
> I'd be inclined to use {} for the list_for_each_entry() call
> 

OK, I will add {}, thanks!

>>       return NULL;
>> @@ -1920,6 +1944,9 @@ bool perf_pmu__has_hybrid(void)
>>   int perf_pmu__match(char *pattern, char *name, char *tok)
>>   {
>> +    if (!name)
>> +        return -1;
>> +
>>       if (fnmatch(pattern, name, 0))
>>           return -1;
>> diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
>> index 926da483a141..f6ca9f6a06ef 100644
>> --- a/tools/perf/util/pmu.h
>> +++ b/tools/perf/util/pmu.h
>> @@ -21,6 +21,7 @@ enum {
>>   #define PERF_PMU_FORMAT_BITS 64
>>   #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
>>   #define CPUS_TEMPLATE_CPU    "%s/bus/event_source/devices/%s/cpus"
>> +#define MAX_PMU_NAME_LEN 128
>>   struct perf_event_attr;
>> @@ -32,6 +33,7 @@ struct perf_pmu_caps {
>>   struct perf_pmu {
>>       char *name;
>> +    char *alias_name;    /* PMU alias name */
> 
> useless comment
> 

OK, remove it :)

Thanks
Jin Yao

>>       char *id;
>>       __u32 type;
>>       bool selectable;
>> @@ -135,4 +137,7 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
>>   bool perf_pmu__has_hybrid(void);
>>   int perf_pmu__match(char *pattern, char *name, char *tok);
>> +char *pmu_find_real_name(const char *name);
>> +char *pmu_find_alias_name(const char *name);
>> +
>>   #endif /* __PMU_H */
>>
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-08-13  1:59 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-11  2:48 [PATCH v4 0/2] perf tools: Add PMU alias support Jin Yao
2021-08-11  2:48 ` [PATCH v4 1/2] perf pmu: " Jin Yao
2021-08-11 13:27   ` Andi Kleen
2021-08-12  4:39     ` Jin, Yao
2021-08-11 19:23   ` Jiri Olsa
2021-08-12  4:32     ` Jin, Yao
2021-08-12 12:14   ` John Garry
2021-08-13  1:59     ` Jin, Yao
2021-08-11  2:48 ` [PATCH v4 2/2] perf tests: Test for PMU alias Jin Yao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).