* [PATCH v2 01/13] perf pmu: Simplify an asprintf error message
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:18 ` Arnaldo Carvalho de Melo
2024-09-12 19:03 ` [PATCH v2 02/13] perf pmu: Allow hardcoded terms to be applied to attributes Ian Rogers
` (12 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Use ifs rather than ?: to avoid a large compound statement.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/pmu.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 61bdda01a05a..779db2ac06f0 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1466,13 +1466,12 @@ static int pmu_config_term(const struct perf_pmu *pmu,
if (err) {
char *err_str;
- parse_events_error__handle(err, term->err_val,
- asprintf(&err_str,
- "value too big for format (%s), maximum is %llu",
- format->name, (unsigned long long)max_val) < 0
- ? strdup("value too big for format")
- : err_str,
- NULL);
+ if (asprintf(&err_str,
+ "value too big for format (%s), maximum is %llu",
+ format->name, (unsigned long long)max_val) < 0) {
+ err_str = strdup("value too big for format");
+ }
+ parse_events_error__handle(err, term->err_val, err_str, /*help=*/NULL);
return -EINVAL;
}
/*
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH v2 01/13] perf pmu: Simplify an asprintf error message
2024-09-12 19:03 ` [PATCH v2 01/13] perf pmu: Simplify an asprintf error message Ian Rogers
@ 2024-09-12 19:18 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 23+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-09-12 19:18 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Adrian Hunter, Kan Liang,
John Garry, Will Deacon, James Clark, Mike Leach, Leo Yan,
Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang, Sandipan Das,
Benjamin Gray, Athira Jajeev, Howard Chu, Dominique Martinet,
Yang Jihong, Colin Ian King, Veronika Molnarova,
Dr. David Alan Gilbert, Oliver Upton, Changbin Du, Ze Gao,
Andi Kleen, Clément Le Goffic, Sun Haiyong, Junhao He,
Tiezhu Yang, Yicong Yang, linux-kernel, linux-perf-users,
linux-arm-kernel
On Thu, Sep 12, 2024 at 12:03:28PM -0700, Ian Rogers wrote:
> Use ifs rather than ?: to avoid a large compound statement.
Cherry picked this one.
- Arnaldo
> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
> tools/perf/util/pmu.c | 13 ++++++-------
> 1 file changed, 6 insertions(+), 7 deletions(-)
>
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index 61bdda01a05a..779db2ac06f0 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -1466,13 +1466,12 @@ static int pmu_config_term(const struct perf_pmu *pmu,
> if (err) {
> char *err_str;
>
> - parse_events_error__handle(err, term->err_val,
> - asprintf(&err_str,
> - "value too big for format (%s), maximum is %llu",
> - format->name, (unsigned long long)max_val) < 0
> - ? strdup("value too big for format")
> - : err_str,
> - NULL);
> + if (asprintf(&err_str,
> + "value too big for format (%s), maximum is %llu",
> + format->name, (unsigned long long)max_val) < 0) {
> + err_str = strdup("value too big for format");
> + }
> + parse_events_error__handle(err, term->err_val, err_str, /*help=*/NULL);
> return -EINVAL;
> }
> /*
> --
> 2.46.0.662.g92d0881bb0-goog
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 02/13] perf pmu: Allow hardcoded terms to be applied to attributes
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
2024-09-12 19:03 ` [PATCH v2 01/13] perf pmu: Simplify an asprintf error message Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 03/13] perf parse-events: Expose/rename config_term_name Ian Rogers
` (11 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Hard coded terms like "config=10" are skipped by perf_pmu__config
assuming they were already applied to a perf_event_attr by parse
event's config_attr function. When doing a reverse number to name
lookup in perf_pmu__name_from_config, as the hardcoded terms aren't
applied the config value is incorrect leading to misses or false
matches. Fix this by adding a parameter to have perf_pmu__config apply
hardcoded terms too (not just in parse event's config_term_common).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/x86/util/intel-pt.c | 3 +-
tools/perf/tests/pmu.c | 3 +-
tools/perf/util/parse-events.c | 4 ++-
tools/perf/util/pmu.c | 56 ++++++++++++++++++++++++-----
tools/perf/util/pmu.h | 4 ++-
5 files changed, 58 insertions(+), 12 deletions(-)
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index ea510a7486b1..8f235d8b67b6 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -75,7 +75,8 @@ static int intel_pt_parse_terms_with_default(const struct perf_pmu *pmu,
goto out_free;
attr.config = *config;
- err = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/true, /*err=*/NULL);
+ err = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/true, /*apply_hardcoded=*/false,
+ /*err=*/NULL);
if (err)
goto out_free;
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c
index be18506f6a24..6a681e3fb552 100644
--- a/tools/perf/tests/pmu.c
+++ b/tools/perf/tests/pmu.c
@@ -176,7 +176,8 @@ static int test__pmu_format(struct test_suite *test __maybe_unused, int subtest
}
memset(&attr, 0, sizeof(attr));
- ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL);
+ ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false,
+ /*apply_hardcoded=*/false, /*err=*/NULL);
if (ret) {
pr_err("perf_pmu__config_terms failed");
goto err_out;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 9a8be1e46d67..26bbc6f603ab 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1537,7 +1537,9 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state,
return -ENOMEM;
}
- if (perf_pmu__config(pmu, &attr, &parsed_terms, parse_state->error)) {
+ /* Skip configuring hard coded terms that were applied by config_attr. */
+ if (perf_pmu__config(pmu, &attr, &parsed_terms, /*apply_hardcoded=*/false,
+ parse_state->error)) {
free_config_terms(&config_terms);
parse_events_terms__exit(&parsed_terms);
return -EINVAL;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 779db2ac06f0..c020fc34b635 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1366,7 +1366,8 @@ static int pmu_config_term(const struct perf_pmu *pmu,
struct perf_event_attr *attr,
struct parse_events_term *term,
struct parse_events_terms *head_terms,
- bool zero, struct parse_events_error *err)
+ bool zero, bool apply_hardcoded,
+ struct parse_events_error *err)
{
struct perf_pmu_format *format;
__u64 *vp;
@@ -1380,11 +1381,46 @@ static int pmu_config_term(const struct perf_pmu *pmu,
return 0;
/*
- * Hardcoded terms should be already in, so nothing
- * to be done for them.
+ * Hardcoded terms are generally handled in event parsing, which
+ * traditionally have had to handle not having a PMU. An alias may
+ * have hard coded config values, optionally apply them below.
*/
- if (parse_events__is_hardcoded_term(term))
+ if (parse_events__is_hardcoded_term(term)) {
+ /* Config terms set all bits in the config. */
+ DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
+
+ if (!apply_hardcoded)
+ return 0;
+
+ bitmap_fill(bits, PERF_PMU_FORMAT_BITS);
+
+ switch (term->type_term) {
+ case PARSE_EVENTS__TERM_TYPE_CONFIG:
+ assert(term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
+ pmu_format_value(bits, term->val.num, &attr->config, zero);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+ assert(term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
+ pmu_format_value(bits, term->val.num, &attr->config1, zero);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+ assert(term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
+ pmu_format_value(bits, term->val.num, &attr->config2, zero);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_CONFIG3:
+ assert(term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
+ pmu_format_value(bits, term->val.num, &attr->config3, zero);
+ break;
+ case PARSE_EVENTS__TERM_TYPE_USER: /* Not hardcoded. */
+ return -EINVAL;
+ case PARSE_EVENTS__TERM_TYPE_NAME ... PARSE_EVENTS__TERM_TYPE_HARDWARE:
+ /* Skip non-config terms. */
+ break;
+ default:
+ break;
+ }
return 0;
+ }
format = pmu_find_format(&pmu->format, term->config);
if (!format) {
@@ -1487,12 +1523,13 @@ static int pmu_config_term(const struct perf_pmu *pmu,
int perf_pmu__config_terms(const struct perf_pmu *pmu,
struct perf_event_attr *attr,
struct parse_events_terms *terms,
- bool zero, struct parse_events_error *err)
+ bool zero, bool apply_hardcoded,
+ struct parse_events_error *err)
{
struct parse_events_term *term;
list_for_each_entry(term, &terms->terms, list) {
- if (pmu_config_term(pmu, attr, term, terms, zero, err))
+ if (pmu_config_term(pmu, attr, term, terms, zero, apply_hardcoded, err))
return -EINVAL;
}
@@ -1506,6 +1543,7 @@ int perf_pmu__config_terms(const struct perf_pmu *pmu,
*/
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct parse_events_terms *head_terms,
+ bool apply_hardcoded,
struct parse_events_error *err)
{
bool zero = !!pmu->perf_event_attr_init_default;
@@ -1514,7 +1552,7 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
if (perf_pmu__is_fake(pmu))
return 0;
- return perf_pmu__config_terms(pmu, attr, head_terms, zero, err);
+ return perf_pmu__config_terms(pmu, attr, head_terms, zero, apply_hardcoded, err);
}
static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
@@ -2279,7 +2317,9 @@ const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config)
pmu_add_cpu_aliases(pmu);
list_for_each_entry(event, &pmu->aliases, list) {
struct perf_event_attr attr = {.config = 0,};
- int ret = perf_pmu__config(pmu, &attr, &event->terms, NULL);
+
+ int ret = perf_pmu__config(pmu, &attr, &event->terms, /*apply_hardcoded=*/true,
+ /*err=*/NULL);
if (ret == 0 && config == attr.config)
return event->name;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 4397c48ad569..af7532ca7fb1 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -206,11 +206,13 @@ typedef int (*pmu_format_callback)(void *state, const char *name, int config,
void pmu_add_sys_aliases(struct perf_pmu *pmu);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct parse_events_terms *head_terms,
+ bool apply_hardcoded,
struct parse_events_error *error);
int perf_pmu__config_terms(const struct perf_pmu *pmu,
struct perf_event_attr *attr,
struct parse_events_terms *terms,
- bool zero, struct parse_events_error *error);
+ bool zero, bool apply_hardcoded,
+ struct parse_events_error *error);
__u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name);
int perf_pmu__format_type(struct perf_pmu *pmu, const char *name);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_terms,
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 03/13] perf parse-events: Expose/rename config_term_name
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
2024-09-12 19:03 ` [PATCH v2 01/13] perf pmu: Simplify an asprintf error message Ian Rogers
2024-09-12 19:03 ` [PATCH v2 02/13] perf pmu: Allow hardcoded terms to be applied to attributes Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 04/13] perf tool_pmu: Factor tool events into their own PMU Ian Rogers
` (10 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Expose config_term_name as parse_events__term_type_str so that PMUs not
in pmu.c may access it.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/parse-events.c | 20 +++++++++++---------
tools/perf/util/parse-events.h | 2 ++
2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 26bbc6f603ab..776812460dad 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -794,7 +794,7 @@ static int check_type_val(struct parse_events_term *term,
static bool config_term_shrinked;
-static const char *config_term_name(enum parse_events__term_type term_type)
+const char *parse_events__term_type_str(enum parse_events__term_type term_type)
{
/*
* Update according to parse-events.l
@@ -880,7 +880,7 @@ config_term_avail(enum parse_events__term_type term_type, struct parse_events_er
/* term_type is validated so indexing is safe */
if (asprintf(&err_str, "'%s' is not usable in 'perf stat'",
- config_term_name(term_type)) >= 0)
+ parse_events__term_type_str(term_type)) >= 0)
parse_events_error__handle(err, -1, err_str, NULL);
return false;
}
@@ -1004,7 +1004,7 @@ do { \
case PARSE_EVENTS__TERM_TYPE_HARDWARE:
default:
parse_events_error__handle(err, term->err_term,
- strdup(config_term_name(term->type_term)),
+ strdup(parse_events__term_type_str(term->type_term)),
parse_events_formats_error_string(NULL));
return -EINVAL;
}
@@ -1127,8 +1127,9 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
default:
if (err) {
parse_events_error__handle(err, term->err_term,
- strdup(config_term_name(term->type_term)),
- strdup("valid terms: call-graph,stack-size\n"));
+ strdup(parse_events__term_type_str(term->type_term)),
+ strdup("valid terms: call-graph,stack-size\n")
+ );
}
return -EINVAL;
}
@@ -2568,7 +2569,7 @@ int parse_events_term__num(struct parse_events_term **term,
struct parse_events_term temp = {
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = type_term,
- .config = config ? : strdup(config_term_name(type_term)),
+ .config = config ? : strdup(parse_events__term_type_str(type_term)),
.no_value = no_value,
.err_term = loc_term ? loc_term->first_column : 0,
.err_val = loc_val ? loc_val->first_column : 0,
@@ -2602,7 +2603,7 @@ int parse_events_term__term(struct parse_events_term **term,
void *loc_term, void *loc_val)
{
return parse_events_term__str(term, term_lhs, NULL,
- strdup(config_term_name(term_rhs)),
+ strdup(parse_events__term_type_str(term_rhs)),
loc_term, loc_val);
}
@@ -2709,7 +2710,8 @@ int parse_events_terms__to_strbuf(const struct parse_events_terms *terms, struct
if (ret < 0)
return ret;
} else if ((unsigned int)term->type_term < __PARSE_EVENTS__TERM_TYPE_NR) {
- ret = strbuf_addf(sb, "%s=", config_term_name(term->type_term));
+ ret = strbuf_addf(sb, "%s=",
+ parse_events__term_type_str(term->type_term));
if (ret < 0)
return ret;
}
@@ -2729,7 +2731,7 @@ static void config_terms_list(char *buf, size_t buf_sz)
buf[0] = '\0';
for (i = 0; i < __PARSE_EVENTS__TERM_TYPE_NR; i++) {
- const char *name = config_term_name(i);
+ const char *name = parse_events__term_type_str(i);
if (!config_term_avail(i, NULL))
continue;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 10cc9c433116..bbe6c5700828 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -162,6 +162,8 @@ struct parse_events_state {
bool wild_card_pmus;
};
+const char *parse_events__term_type_str(enum parse_events__term_type term_type);
+
bool parse_events__filter_pmu(const struct parse_events_state *parse_state,
const struct perf_pmu *pmu);
void parse_events__shrink_config_terms(void);
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 04/13] perf tool_pmu: Factor tool events into their own PMU
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (2 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 03/13] perf parse-events: Expose/rename config_term_name Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 05/13] perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event Ian Rogers
` (9 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Rather than treat tool events as a special kind of event, create a
tool only PMU where the events/aliases match the existing
duration_time, user_time and system_time events. Remove special
parsing and printing support for the tool events, but add function
calls for when PMU functions are called on a tool_pmu.
Move the tool PMU code in evsel into tool_pmu.c to better encapsulate
the tool event behavior in that file.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-list.c | 13 +-
tools/perf/builtin-stat.c | 1 +
tools/perf/util/Build | 1 +
tools/perf/util/evsel.c | 272 ++--------------------
tools/perf/util/evsel.h | 28 +--
tools/perf/util/metricgroup.c | 1 +
tools/perf/util/parse-events.c | 38 ---
tools/perf/util/parse-events.h | 3 -
tools/perf/util/parse-events.l | 11 -
tools/perf/util/parse-events.y | 16 --
tools/perf/util/pmu.c | 20 +-
tools/perf/util/pmu.h | 2 +
tools/perf/util/pmus.c | 9 +
tools/perf/util/print-events.c | 36 +--
tools/perf/util/print-events.h | 1 -
tools/perf/util/stat-display.c | 6 +-
tools/perf/util/stat-shadow.c | 1 +
tools/perf/util/tool_pmu.c | 413 +++++++++++++++++++++++++++++++++
tools/perf/util/tool_pmu.h | 51 ++++
19 files changed, 532 insertions(+), 391 deletions(-)
create mode 100644 tools/perf/util/tool_pmu.c
create mode 100644 tools/perf/util/tool_pmu.h
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 65b8cba324be..b8378ba18c28 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -19,6 +19,7 @@
#include "util/string2.h"
#include "util/strlist.h"
#include "util/strbuf.h"
+#include "util/tool_pmu.h"
#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include <linux/zalloc.h>
@@ -614,9 +615,18 @@ int cmd_list(int argc, const char **argv)
event_symbols_hw, PERF_COUNT_HW_MAX);
else if (strcmp(argv[i], "sw") == 0 ||
strcmp(argv[i], "software") == 0) {
+ char *old_pmu_glob = default_ps.pmu_glob;
+
print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX);
- print_tool_events(&print_cb, ps);
+ default_ps.pmu_glob = strdup("tool");
+ if (!default_ps.pmu_glob) {
+ ret = -1;
+ goto out;
+ }
+ perf_pmus__print_pmu_events(&print_cb, ps);
+ zfree(&default_ps.pmu_glob);
+ default_ps.pmu_glob = old_pmu_glob;
} else if (strcmp(argv[i], "cache") == 0 ||
strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(&print_cb, ps);
@@ -664,7 +674,6 @@ int cmd_list(int argc, const char **argv)
event_symbols_hw, PERF_COUNT_HW_MAX);
print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX);
- print_tool_events(&print_cb, ps);
print_hwcache_events(&print_cb, ps);
perf_pmus__print_pmu_events(&print_cb, ps);
print_tracepoint_events(&print_cb, ps);
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 689a3d43c258..ae525fdcfbde 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -46,6 +46,7 @@
#include "util/parse-events.h"
#include "util/pmus.h"
#include "util/pmu.h"
+#include "util/tool_pmu.h"
#include "util/event.h"
#include "util/evlist.h"
#include "util/evsel.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index dc616292b2dd..fa508e113dd0 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -83,6 +83,7 @@ perf-util-y += pmu.o
perf-util-y += pmus.o
perf-util-y += pmu-flex.o
perf-util-y += pmu-bison.o
+perf-util-y += tool_pmu.o
perf-util-y += svghelper.o
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-info.o
perf-util-y += trace-event-scripting.o
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index dbf9c8cee3c5..b75c4a4c7aa7 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -10,7 +10,6 @@
#include <errno.h>
#include <inttypes.h>
#include <linux/bitops.h>
-#include <api/io.h>
#include <api/fs/fs.h>
#include <api/fs/tracing_path.h>
#include <linux/hw_breakpoint.h>
@@ -51,6 +50,7 @@
#include "off_cpu.h"
#include "pmu.h"
#include "pmus.h"
+#include "tool_pmu.h"
#include "rlimit.h"
#include "../perf-sys.h"
#include "util/parse-branch-options.h"
@@ -71,33 +71,6 @@ struct perf_missing_features perf_missing_features;
static clockid_t clockid;
-static const char *const perf_tool_event__tool_names[PERF_TOOL_MAX] = {
- NULL,
- "duration_time",
- "user_time",
- "system_time",
-};
-
-const char *perf_tool_event__to_str(enum perf_tool_event ev)
-{
- if (ev > PERF_TOOL_NONE && ev < PERF_TOOL_MAX)
- return perf_tool_event__tool_names[ev];
-
- return NULL;
-}
-
-enum perf_tool_event perf_tool_event__from_str(const char *str)
-{
- int i;
-
- perf_tool_event__for_each_event(i) {
- if (!strcmp(str, perf_tool_event__tool_names[i]))
- return i;
- }
- return PERF_TOOL_NONE;
-}
-
-
static int evsel__no_extra_init(struct evsel *evsel __maybe_unused)
{
return 0;
@@ -421,7 +394,6 @@ struct evsel *evsel__clone(struct evsel *orig)
evsel->core.leader = orig->core.leader;
evsel->max_events = orig->max_events;
- evsel->tool_event = orig->tool_event;
free((char *)evsel->unit);
evsel->unit = strdup(orig->unit);
if (evsel->unit == NULL)
@@ -617,11 +589,6 @@ static int evsel__sw_name(struct evsel *evsel, char *bf, size_t size)
return r + evsel__add_modifiers(evsel, bf + r, size - r);
}
-static int evsel__tool_name(enum perf_tool_event ev, char *bf, size_t size)
-{
- return scnprintf(bf, size, "%s", perf_tool_event__to_str(ev));
-}
-
static int __evsel__bp_name(char *bf, size_t size, u64 addr, u64 type)
{
int r;
@@ -772,10 +739,7 @@ const char *evsel__name(struct evsel *evsel)
break;
case PERF_TYPE_SOFTWARE:
- if (evsel__is_tool(evsel))
- evsel__tool_name(evsel__tool_event(evsel), bf, sizeof(bf));
- else
- evsel__sw_name(evsel, bf, sizeof(bf));
+ evsel__sw_name(evsel, bf, sizeof(bf));
break;
case PERF_TYPE_TRACEPOINT:
@@ -786,6 +750,10 @@ const char *evsel__name(struct evsel *evsel)
evsel__bp_name(evsel, bf, sizeof(bf));
break;
+ case PERF_PMU_TYPE_TOOL:
+ scnprintf(bf, sizeof(bf), "%s", evsel__tool_pmu_event_name(evsel));
+ break;
+
default:
scnprintf(bf, sizeof(bf), "unknown attr type: %d",
evsel->core.attr.type);
@@ -811,7 +779,7 @@ const char *evsel__metric_id(const struct evsel *evsel)
return evsel->metric_id;
if (evsel__is_tool(evsel))
- return perf_tool_event__to_str(evsel__tool_event(evsel));
+ return evsel__tool_pmu_event_name(evsel);
return "unknown";
}
@@ -1684,167 +1652,6 @@ static int evsel__read_group(struct evsel *leader, int cpu_map_idx, int thread)
return evsel__process_group_data(leader, cpu_map_idx, thread, data);
}
-static bool read_until_char(struct io *io, char e)
-{
- int c;
-
- do {
- c = io__get_char(io);
- if (c == -1)
- return false;
- } while (c != e);
- return true;
-}
-
-static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val)
-{
- char buf[256];
- struct io io;
- int i;
-
- io__init(&io, fd, buf, sizeof(buf));
-
- /* Skip lines to relevant CPU. */
- for (i = -1; i < cpu.cpu; i++) {
- if (!read_until_char(&io, '\n'))
- return -EINVAL;
- }
- /* Skip to "cpu". */
- if (io__get_char(&io) != 'c') return -EINVAL;
- if (io__get_char(&io) != 'p') return -EINVAL;
- if (io__get_char(&io) != 'u') return -EINVAL;
-
- /* Skip N of cpuN. */
- if (!read_until_char(&io, ' '))
- return -EINVAL;
-
- i = 1;
- while (true) {
- if (io__get_dec(&io, val) != ' ')
- break;
- if (field == i)
- return 0;
- i++;
- }
- return -EINVAL;
-}
-
-static int read_pid_stat_field(int fd, int field, __u64 *val)
-{
- char buf[256];
- struct io io;
- int c, i;
-
- io__init(&io, fd, buf, sizeof(buf));
- if (io__get_dec(&io, val) != ' ')
- return -EINVAL;
- if (field == 1)
- return 0;
-
- /* Skip comm. */
- if (io__get_char(&io) != '(' || !read_until_char(&io, ')'))
- return -EINVAL;
- if (field == 2)
- return -EINVAL; /* String can't be returned. */
-
- /* Skip state */
- if (io__get_char(&io) != ' ' || io__get_char(&io) == -1)
- return -EINVAL;
- if (field == 3)
- return -EINVAL; /* String can't be returned. */
-
- /* Loop over numeric fields*/
- if (io__get_char(&io) != ' ')
- return -EINVAL;
-
- i = 4;
- while (true) {
- c = io__get_dec(&io, val);
- if (c == -1)
- return -EINVAL;
- if (c == -2) {
- /* Assume a -ve was read */
- c = io__get_dec(&io, val);
- *val *= -1;
- }
- if (c != ' ')
- return -EINVAL;
- if (field == i)
- return 0;
- i++;
- }
- return -EINVAL;
-}
-
-static int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
-{
- __u64 *start_time, cur_time, delta_start;
- int fd, err = 0;
- struct perf_counts_values *count;
- bool adjust = false;
-
- count = perf_counts(evsel->counts, cpu_map_idx, thread);
-
- switch (evsel__tool_event(evsel)) {
- case PERF_TOOL_DURATION_TIME:
- /*
- * Pretend duration_time is only on the first CPU and thread, or
- * else aggregation will scale duration_time by the number of
- * CPUs/threads.
- */
- start_time = &evsel->start_time;
- if (cpu_map_idx == 0 && thread == 0)
- cur_time = rdclock();
- else
- cur_time = *start_time;
- break;
- case PERF_TOOL_USER_TIME:
- case PERF_TOOL_SYSTEM_TIME: {
- bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME;
-
- start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread);
- fd = FD(evsel, cpu_map_idx, thread);
- lseek(fd, SEEK_SET, 0);
- if (evsel->pid_stat) {
- /* The event exists solely on 1 CPU. */
- if (cpu_map_idx == 0)
- err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time);
- else
- cur_time = 0;
- } else {
- /* The event is for all threads. */
- if (thread == 0) {
- struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus,
- cpu_map_idx);
-
- err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time);
- } else {
- cur_time = 0;
- }
- }
- adjust = true;
- break;
- }
- case PERF_TOOL_NONE:
- case PERF_TOOL_MAX:
- default:
- err = -EINVAL;
- }
- if (err)
- return err;
-
- delta_start = cur_time - *start_time;
- if (adjust) {
- __u64 ticks_per_sec = sysconf(_SC_CLK_TCK);
-
- delta_start *= 1000000000 / ticks_per_sec;
- }
- count->val = delta_start;
- count->ena = count->run = delta_start;
- count->lost = 0;
- return 0;
-}
-
int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread)
{
if (evsel__is_tool(evsel))
@@ -2042,6 +1849,7 @@ static struct perf_thread_map *empty_thread_map;
static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,
struct perf_thread_map *threads)
{
+ int ret = 0;
int nthreads = perf_thread_map__nr(threads);
if ((perf_missing_features.write_backward && evsel->core.attr.write_backward) ||
@@ -2072,19 +1880,14 @@ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,
perf_evsel__alloc_fd(&evsel->core, perf_cpu_map__nr(cpus), nthreads) < 0)
return -ENOMEM;
- if ((evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME ||
- evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) &&
- !evsel->start_times) {
- evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64));
- if (!evsel->start_times)
- return -ENOMEM;
- }
+ if (evsel__is_tool(evsel))
+ ret = evsel__tool_pmu_prepare_open(evsel, cpus, nthreads);
evsel->open_flags = PERF_FLAG_FD_CLOEXEC;
if (evsel->cgrp)
evsel->open_flags |= PERF_FLAG_PID_CGROUP;
- return 0;
+ return ret;
}
static void evsel__disable_missing_features(struct evsel *evsel)
@@ -2262,13 +2065,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
int pid = -1, err, old_errno;
enum rlimit_action set_rlimit = NO_CHANGE;
- if (evsel__tool_event(evsel) == PERF_TOOL_DURATION_TIME) {
- if (evsel->core.attr.sample_period) /* no sampling */
- return -EINVAL;
- evsel->start_time = rdclock();
- return 0;
- }
-
if (evsel__is_retire_lat(evsel))
return tpebs_start(evsel->evlist);
@@ -2293,6 +2089,12 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
pr_debug3("Opening: %s\n", evsel__name(evsel));
display_attr(&evsel->core.attr);
+ if (evsel__is_tool(evsel)) {
+ return evsel__tool_pmu_open(evsel, threads,
+ start_cpu_map_idx,
+ end_cpu_map_idx);
+ }
+
for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
for (thread = 0; thread < nthreads; thread++) {
@@ -2304,46 +2106,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
if (!evsel->cgrp && !evsel->core.system_wide)
pid = perf_thread_map__pid(threads, thread);
- if (evsel__tool_event(evsel) == PERF_TOOL_USER_TIME ||
- evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME) {
- bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME;
- __u64 *start_time = NULL;
-
- if (evsel->core.attr.sample_period) {
- /* no sampling */
- err = -EINVAL;
- goto out_close;
- }
- if (pid > -1) {
- char buf[64];
-
- snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
- fd = open(buf, O_RDONLY);
- evsel->pid_stat = true;
- } else {
- fd = open("/proc/stat", O_RDONLY);
- }
- FD(evsel, idx, thread) = fd;
- if (fd < 0) {
- err = -errno;
- goto out_close;
- }
- start_time = xyarray__entry(evsel->start_times, idx, thread);
- if (pid > -1) {
- err = read_pid_stat_field(fd, system ? 15 : 14,
- start_time);
- } else {
- struct perf_cpu cpu;
-
- cpu = perf_cpu_map__cpu(evsel->core.cpus, idx);
- err = read_stat_field(fd, cpu, system ? 3 : 1,
- start_time);
- }
- if (err)
- goto out_close;
- continue;
- }
-
group_fd = get_group_fd(evsel, idx, thread);
if (group_fd == -2) {
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 15e745a9a798..bc30491bd490 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -11,6 +11,7 @@
#include <perf/evsel.h>
#include "symbol_conf.h"
#include "pmus.h"
+#include "pmu.h"
struct bpf_object;
struct cgroup;
@@ -22,25 +23,9 @@ struct target;
struct hashmap;
struct bperf_leader_bpf;
struct bperf_follower_bpf;
-struct perf_pmu;
typedef int (evsel__sb_cb_t)(union perf_event *event, void *data);
-enum perf_tool_event {
- PERF_TOOL_NONE = 0,
- PERF_TOOL_DURATION_TIME = 1,
- PERF_TOOL_USER_TIME = 2,
- PERF_TOOL_SYSTEM_TIME = 3,
-
- PERF_TOOL_MAX,
-};
-
-const char *perf_tool_event__to_str(enum perf_tool_event ev);
-enum perf_tool_event perf_tool_event__from_str(const char *str);
-
-#define perf_tool_event__for_each_event(ev) \
- for ((ev) = PERF_TOOL_DURATION_TIME; (ev) < PERF_TOOL_MAX; ev++)
-
/** struct evsel - event selector
*
* @evlist - evlist this evsel is in, if it is in one.
@@ -83,7 +68,6 @@ struct evsel {
const char *unit;
struct cgroup *cgrp;
const char *metric_id;
- enum perf_tool_event tool_event;
/* parse modifier helper */
int exclude_GH;
int sample_read;
@@ -320,21 +304,11 @@ const char *evsel__name(struct evsel *evsel);
bool evsel__name_is(struct evsel *evsel, const char *name);
const char *evsel__metric_id(const struct evsel *evsel);
-static inline bool evsel__is_tool(const struct evsel *evsel)
-{
- return evsel->tool_event != PERF_TOOL_NONE;
-}
-
static inline bool evsel__is_retire_lat(const struct evsel *evsel)
{
return evsel->retire_lat;
}
-static inline enum perf_tool_event evsel__tool_event(const struct evsel *evsel)
-{
- return evsel->tool_event;
-}
-
const char *evsel__group_name(struct evsel *evsel);
int evsel__group_desc(struct evsel *evsel, char *buf, size_t size);
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 4dff3e925a47..9181548e8881 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -14,6 +14,7 @@
#include "pmus.h"
#include "print-events.h"
#include "smt.h"
+#include "tool_pmu.h"
#include "expr.h"
#include "rblist.h"
#include <string.h>
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 776812460dad..cb5ecc6bfdc3 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -298,37 +298,6 @@ static int add_event(struct list_head *list, int *idx,
/*auto_merge_stats=*/false, /*cpu_list=*/NULL) ? 0 : -ENOMEM;
}
-static int add_event_tool(struct list_head *list, int *idx,
- enum perf_tool_event tool_event)
-{
- struct evsel *evsel;
- struct perf_event_attr attr = {
- .type = PERF_TYPE_SOFTWARE,
- .config = PERF_COUNT_SW_DUMMY,
- };
- struct perf_cpu_map *cpu_list = NULL;
-
- if (tool_event == PERF_TOOL_DURATION_TIME) {
- /* Duration time is gathered globally, pretend it is only on CPU0. */
- cpu_list = perf_cpu_map__new("0");
- }
- evsel = __add_event(list, idx, &attr, /*init_attr=*/true, /*name=*/NULL,
- /*metric_id=*/NULL, /*pmu=*/NULL,
- /*config_terms=*/NULL, /*auto_merge_stats=*/false,
- cpu_list);
- perf_cpu_map__put(cpu_list);
- if (!evsel)
- return -ENOMEM;
- evsel->tool_event = tool_event;
- if (tool_event == PERF_TOOL_DURATION_TIME
- || tool_event == PERF_TOOL_USER_TIME
- || tool_event == PERF_TOOL_SYSTEM_TIME) {
- free((char *)evsel->unit);
- evsel->unit = strdup("ns");
- }
- return 0;
-}
-
/**
* parse_aliases - search names for entries beginning or equalling str ignoring
* case. If mutliple entries in names match str then the longest
@@ -1422,13 +1391,6 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
type, /*extended_type=*/0, config, head_config);
}
-int parse_events_add_tool(struct parse_events_state *parse_state,
- struct list_head *list,
- int tool_event)
-{
- return add_event_tool(list, &parse_state->idx, tool_event);
-}
-
static bool config_term_percore(struct list_head *config_terms)
{
struct evsel_config_term *term;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index bbe6c5700828..336ef5df13c5 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -223,9 +223,6 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
u32 type, u64 config,
const struct parse_events_terms *head_config,
bool wildcard);
-int parse_events_add_tool(struct parse_events_state *parse_state,
- struct list_head *list,
- int tool_event);
int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
struct parse_events_state *parse_state,
struct parse_events_terms *parsed_terms);
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 5a0bcd7f166a..14e5bd856a18 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -121,14 +121,6 @@ static int sym(yyscan_t scanner, int type, int config)
return type == PERF_TYPE_HARDWARE ? PE_VALUE_SYM_HW : PE_VALUE_SYM_SW;
}
-static int tool(yyscan_t scanner, enum perf_tool_event event)
-{
- YYSTYPE *yylval = parse_events_get_lval(scanner);
-
- yylval->num = event;
- return PE_VALUE_SYM_TOOL;
-}
-
static int term(yyscan_t scanner, enum parse_events__term_type type)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
@@ -404,9 +396,6 @@ cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COU
alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
-duration_time { return tool(yyscanner, PERF_TOOL_DURATION_TIME); }
-user_time { return tool(yyscanner, PERF_TOOL_USER_TIME); }
-system_time { return tool(yyscanner, PERF_TOOL_SYSTEM_TIME); }
bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); }
cgroup-switches { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CGROUP_SWITCHES); }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index b3c51f06cbdc..5c4669264177 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -56,7 +56,6 @@ static void free_list_evsel(struct list_head* list_evsel)
%token PE_START_EVENTS PE_START_TERMS
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_TERM
-%token PE_VALUE_SYM_TOOL
%token PE_EVENT_NAME
%token PE_RAW PE_NAME
%token PE_MODIFIER_EVENT PE_MODIFIER_BP PE_BP_COLON PE_BP_SLASH
@@ -68,7 +67,6 @@ static void free_list_evsel(struct list_head* list_evsel)
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM_HW
%type <num> PE_VALUE_SYM_SW
-%type <num> PE_VALUE_SYM_TOOL
%type <mod> PE_MODIFIER_EVENT
%type <term_type> PE_TERM
%type <num> value_sym
@@ -350,20 +348,6 @@ value_sym sep_slash_slash_dc
PE_ABORT(err);
$$ = list;
}
-|
-PE_VALUE_SYM_TOOL sep_slash_slash_dc
-{
- struct list_head *list;
- int err;
-
- list = alloc_list();
- if (!list)
- YYNOMEM;
- err = parse_events_add_tool(_parse_state, list, $1);
- if (err)
- YYNOMEM;
- $$ = list;
-}
event_legacy_cache:
PE_LEGACY_CACHE opt_event_config
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index c020fc34b635..f17e21455498 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -19,6 +19,7 @@
#include "evsel.h"
#include "pmu.h"
#include "pmus.h"
+#include "tool_pmu.h"
#include <util/pmu-bison.h>
#include <util/pmu-flex.h>
#include "parse-events.h"
@@ -1548,6 +1549,9 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
{
bool zero = !!pmu->perf_event_attr_init_default;
+ if (perf_pmu__is_tool(pmu))
+ return tool_pmu__config_terms(attr, head_terms, err);
+
/* Fake PMU doesn't have proper terms so nothing to configure in attr. */
if (perf_pmu__is_fake(pmu))
return 0;
@@ -1660,8 +1664,8 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_
info->scale = 0.0;
info->snapshot = false;
- /* Fake PMU doesn't rewrite terms. */
- if (perf_pmu__is_fake(pmu))
+ /* Tool/fake PMU doesn't rewrite terms. */
+ if (perf_pmu__is_tool(pmu) || perf_pmu__is_fake(pmu))
goto out;
list_for_each_entry_safe(term, h, &head_terms->terms, list) {
@@ -1827,6 +1831,8 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
{
if (!name)
return false;
+ if (perf_pmu__is_tool(pmu))
+ return perf_tool_event__from_str(name) != PERF_TOOL_NONE;
if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
return true;
if (pmu->cpu_aliases_added || !pmu->events_table)
@@ -1838,6 +1844,9 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu)
{
size_t nr;
+ if (perf_pmu__is_tool(pmu))
+ return tool_pmu__num_events();
+
pmu_aliases_parse(pmu);
nr = pmu->sysfs_aliases + pmu->sys_json_aliases;
@@ -1898,6 +1907,9 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
int ret = 0;
struct strbuf sb;
+ if (perf_pmu__is_tool(pmu))
+ return tool_pmu__for_each_event_cb(pmu, state, cb);
+
strbuf_init(&sb, /*hint=*/ 0);
pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu);
@@ -1986,6 +1998,7 @@ bool perf_pmu__is_software(const struct perf_pmu *pmu)
case PERF_TYPE_HW_CACHE: return false;
case PERF_TYPE_RAW: return false;
case PERF_TYPE_BREAKPOINT: return true;
+ case PERF_PMU_TYPE_TOOL: return true;
default: break;
}
for (size_t i = 0; i < ARRAY_SIZE(known_sw_pmus); i++) {
@@ -2313,6 +2326,9 @@ const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config)
if (!pmu)
return NULL;
+ if (perf_pmu__is_tool(pmu))
+ return perf_tool_event__to_str(config);
+
pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu);
list_for_each_entry(event, &pmu->aliases, list) {
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index af7532ca7fb1..880a8dc661b7 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -37,6 +37,7 @@ struct perf_pmu_caps {
};
enum {
+ PERF_PMU_TYPE_TOOL = 0xFFFFFFFE,
PERF_PMU_TYPE_FAKE = 0xFFFFFFFF,
};
@@ -282,6 +283,7 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus);
void perf_pmu__delete(struct perf_pmu *pmu);
struct perf_pmu *perf_pmus__find_core_pmu(void);
+
const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config);
#endif /* __PMU_H */
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 52109af5f2f1..6e657c44de58 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -15,6 +15,7 @@
#include "evsel.h"
#include "pmus.h"
#include "pmu.h"
+#include "tool_pmu.h"
#include "print-events.h"
#include "strbuf.h"
@@ -200,6 +201,7 @@ static void pmu_read_sysfs(bool core_only)
int fd;
DIR *dir;
struct dirent *dent;
+ struct perf_pmu *tool_pmu;
if (read_sysfs_all_pmus || (core_only && read_sysfs_core_pmus))
return;
@@ -229,6 +231,10 @@ static void pmu_read_sysfs(bool core_only)
pr_err("Failure to set up any core PMUs\n");
}
list_sort(NULL, &core_pmus, pmus_cmp);
+ if (!core_only) {
+ tool_pmu = perf_pmus__tool_pmu();
+ list_add_tail(&tool_pmu->list, &other_pmus);
+ }
list_sort(NULL, &other_pmus, pmus_cmp);
if (!list_empty(&core_pmus)) {
read_sysfs_core_pmus = true;
@@ -584,6 +590,9 @@ void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, voi
int len = pmu_name_len_no_suffix(pmu->name);
const char *desc = "(see 'man perf-list' or 'man perf-record' on how to encode it)";
+ if (perf_pmu__is_tool(pmu))
+ continue;
+
if (!pmu->is_core)
desc = NULL;
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
index 81e0135cddf0..a786cbfb0ff5 100644
--- a/tools/perf/util/print-events.c
+++ b/tools/perf/util/print-events.c
@@ -29,6 +29,7 @@
#include "tracepoint.h"
#include "pfm.h"
#include "thread_map.h"
+#include "tool_pmu.h"
#include "util.h"
#define MAX_NAME_LEN 100
@@ -43,21 +44,6 @@ static const char * const event_type_descriptors[] = {
"Hardware breakpoint",
};
-static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = {
- [PERF_TOOL_DURATION_TIME] = {
- .symbol = "duration_time",
- .alias = "",
- },
- [PERF_TOOL_USER_TIME] = {
- .symbol = "user_time",
- .alias = "",
- },
- [PERF_TOOL_SYSTEM_TIME] = {
- .symbol = "system_time",
- .alias = "",
- },
-};
-
/*
* Print the events from <debugfs_mount_point>/tracing/events
*/
@@ -341,24 +327,6 @@ int print_hwcache_events(const struct print_callbacks *print_cb, void *print_sta
return 0;
}
-void print_tool_events(const struct print_callbacks *print_cb, void *print_state)
-{
- // Start at 1 because the first enum entry means no tool event.
- for (int i = 1; i < PERF_TOOL_MAX; ++i) {
- print_cb->print_event(print_state,
- "tool",
- /*pmu_name=*/NULL,
- event_symbols_tool[i].symbol,
- event_symbols_tool[i].alias,
- /*scale_unit=*/NULL,
- /*deprecated=*/false,
- "Tool event",
- /*desc=*/NULL,
- /*long_desc=*/NULL,
- /*encoding_desc=*/NULL);
- }
-}
-
void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
unsigned int type, const struct event_symbol *syms,
unsigned int max)
@@ -422,8 +390,6 @@ void print_events(const struct print_callbacks *print_cb, void *print_state)
print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX);
- print_tool_events(print_cb, print_state);
-
print_hwcache_events(print_cb, print_state);
perf_pmus__print_pmu_events(print_cb, print_state);
diff --git a/tools/perf/util/print-events.h b/tools/perf/util/print-events.h
index bf4290bef0cd..445efa1636c1 100644
--- a/tools/perf/util/print-events.h
+++ b/tools/perf/util/print-events.h
@@ -36,7 +36,6 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)
void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
unsigned int type, const struct event_symbol *syms,
unsigned int max);
-void print_tool_events(const struct print_callbacks *print_cb, void *print_state);
void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state);
bool is_event_supported(u8 type, u64 config);
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index ea96e4ebad8c..a82a8ec79b39 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -21,6 +21,7 @@
#include "iostat.h"
#include "pmu.h"
#include "pmus.h"
+#include "tool_pmu.h"
#define CNTR_NOT_SUPPORTED "<not supported>"
#define CNTR_NOT_COUNTED "<not counted>"
@@ -946,7 +947,10 @@ static bool should_skip_zero_counter(struct perf_stat_config *config,
if (config->aggr_mode == AGGR_THREAD && config->system_wide)
return true;
- /* Tool events have the software PMU but are only gathered on 1. */
+ /*
+ * Many tool events are only gathered on the first index, skip other
+ * zero values.
+ */
if (evsel__is_tool(counter))
return true;
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index 99376c12dd8e..2d653f91400e 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -15,6 +15,7 @@
#include <linux/zalloc.h>
#include "iostat.h"
#include "util/hashmap.h"
+#include "tool_pmu.h"
struct stats walltime_nsecs_stats;
struct rusage_stats ru_stats;
diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c
new file mode 100644
index 000000000000..702c1131a2a8
--- /dev/null
+++ b/tools/perf/util/tool_pmu.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "cgroup.h"
+#include "counts.h"
+#include "evsel.h"
+#include "pmu.h"
+#include "print-events.h"
+#include "time-utils.h"
+#include "tool_pmu.h"
+#include <api/io.h>
+#include <internal/threadmap.h>
+#include <perf/threadmap.h>
+#include <fcntl.h>
+#include <strings.h>
+
+static const char *const tool_pmu__event_names[PERF_TOOL_MAX] = {
+ NULL,
+ "duration_time",
+ "user_time",
+ "system_time",
+};
+
+
+const char *perf_tool_event__to_str(enum perf_tool_event ev)
+{
+ if (ev > PERF_TOOL_NONE && ev < PERF_TOOL_MAX)
+ return tool_pmu__event_names[ev];
+
+ return NULL;
+}
+
+enum perf_tool_event perf_tool_event__from_str(const char *str)
+{
+ int i;
+
+ perf_tool_event__for_each_event(i) {
+ if (!strcasecmp(str, tool_pmu__event_names[i]))
+ return i;
+ }
+ return PERF_TOOL_NONE;
+}
+
+static int tool_pmu__config_term(struct perf_event_attr *attr,
+ struct parse_events_term *term,
+ struct parse_events_error *err)
+{
+ if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
+ enum perf_tool_event ev = perf_tool_event__from_str(term->config);
+
+ if (ev == PERF_TOOL_NONE)
+ goto err_out;
+
+ attr->config = ev;
+ return 0;
+ }
+err_out:
+ if (err) {
+ char *err_str;
+
+ parse_events_error__handle(err, term->err_val,
+ asprintf(&err_str,
+ "unexpected tool event term (%s) %s",
+ parse_events__term_type_str(term->type_term),
+ term->config) < 0
+ ? strdup("unexpected tool event term")
+ : err_str,
+ NULL);
+ }
+ return -EINVAL;
+}
+
+int tool_pmu__config_terms(struct perf_event_attr *attr,
+ struct parse_events_terms *terms,
+ struct parse_events_error *err)
+{
+ struct parse_events_term *term;
+
+ list_for_each_entry(term, &terms->terms, list) {
+ if (tool_pmu__config_term(attr, term, err))
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
+{
+ struct pmu_event_info info = {
+ .pmu = pmu,
+ .event_type_desc = "Tool event",
+ };
+ int i;
+
+ perf_tool_event__for_each_event(i) {
+ int ret;
+
+ info.name = perf_tool_event__to_str(i);
+ info.alias = NULL;
+ info.scale_unit = NULL;
+ info.desc = NULL;
+ info.long_desc = NULL;
+ info.encoding_desc = NULL;
+ info.topic = NULL;
+ info.pmu_name = pmu->name;
+ info.deprecated = false;
+ ret = cb(state, &info);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+bool perf_pmu__is_tool(const struct perf_pmu *pmu)
+{
+ return pmu && pmu->type == PERF_PMU_TYPE_TOOL;
+}
+
+bool evsel__is_tool(const struct evsel *evsel)
+{
+ return perf_pmu__is_tool(evsel->pmu);
+}
+
+enum perf_tool_event evsel__tool_event(const struct evsel *evsel)
+{
+ if (!evsel__is_tool(evsel))
+ return PERF_TOOL_NONE;
+
+ return (enum perf_tool_event)evsel->core.attr.config;
+}
+
+const char *evsel__tool_pmu_event_name(const struct evsel *evsel)
+{
+ return perf_tool_event__to_str(evsel->core.attr.config);
+}
+
+static bool read_until_char(struct io *io, char e)
+{
+ int c;
+
+ do {
+ c = io__get_char(io);
+ if (c == -1)
+ return false;
+ } while (c != e);
+ return true;
+}
+
+static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val)
+{
+ char buf[256];
+ struct io io;
+ int i;
+
+ io__init(&io, fd, buf, sizeof(buf));
+
+ /* Skip lines to relevant CPU. */
+ for (i = -1; i < cpu.cpu; i++) {
+ if (!read_until_char(&io, '\n'))
+ return -EINVAL;
+ }
+ /* Skip to "cpu". */
+ if (io__get_char(&io) != 'c') return -EINVAL;
+ if (io__get_char(&io) != 'p') return -EINVAL;
+ if (io__get_char(&io) != 'u') return -EINVAL;
+
+ /* Skip N of cpuN. */
+ if (!read_until_char(&io, ' '))
+ return -EINVAL;
+
+ i = 1;
+ while (true) {
+ if (io__get_dec(&io, val) != ' ')
+ break;
+ if (field == i)
+ return 0;
+ i++;
+ }
+ return -EINVAL;
+}
+
+static int read_pid_stat_field(int fd, int field, __u64 *val)
+{
+ char buf[256];
+ struct io io;
+ int c, i;
+
+ io__init(&io, fd, buf, sizeof(buf));
+ if (io__get_dec(&io, val) != ' ')
+ return -EINVAL;
+ if (field == 1)
+ return 0;
+
+ /* Skip comm. */
+ if (io__get_char(&io) != '(' || !read_until_char(&io, ')'))
+ return -EINVAL;
+ if (field == 2)
+ return -EINVAL; /* String can't be returned. */
+
+ /* Skip state */
+ if (io__get_char(&io) != ' ' || io__get_char(&io) == -1)
+ return -EINVAL;
+ if (field == 3)
+ return -EINVAL; /* String can't be returned. */
+
+ /* Loop over numeric fields*/
+ if (io__get_char(&io) != ' ')
+ return -EINVAL;
+
+ i = 4;
+ while (true) {
+ c = io__get_dec(&io, val);
+ if (c == -1)
+ return -EINVAL;
+ if (c == -2) {
+ /* Assume a -ve was read */
+ c = io__get_dec(&io, val);
+ *val *= -1;
+ }
+ if (c != ' ')
+ return -EINVAL;
+ if (field == i)
+ return 0;
+ i++;
+ }
+ return -EINVAL;
+}
+
+
+
+int evsel__tool_pmu_prepare_open(struct evsel *evsel,
+ struct perf_cpu_map *cpus,
+ int nthreads)
+{
+ if ((evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME ||
+ evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) &&
+ !evsel->start_times) {
+ evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus),
+ nthreads,
+ sizeof(__u64));
+ if (!evsel->start_times)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
+
+int evsel__tool_pmu_open(struct evsel *evsel,
+ struct perf_thread_map *threads,
+ int start_cpu_map_idx, int end_cpu_map_idx)
+{
+ enum perf_tool_event ev = evsel__tool_event(evsel);
+ int pid = -1, idx = 0, thread = 0, nthreads, err = 0, old_errno;
+
+ if (ev == PERF_TOOL_DURATION_TIME) {
+ if (evsel->core.attr.sample_period) /* no sampling */
+ return -EINVAL;
+ evsel->start_time = rdclock();
+ return 0;
+ }
+
+ if (evsel->cgrp)
+ pid = evsel->cgrp->fd;
+
+ nthreads = perf_thread_map__nr(threads);
+ for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
+ for (thread = 0; thread < nthreads; thread++) {
+ if (thread >= nthreads)
+ break;
+
+ if (!evsel->cgrp && !evsel->core.system_wide)
+ pid = perf_thread_map__pid(threads, thread);
+
+ if (ev == PERF_TOOL_USER_TIME || ev == PERF_TOOL_SYSTEM_TIME) {
+ bool system = ev == PERF_TOOL_SYSTEM_TIME;
+ __u64 *start_time = NULL;
+ int fd;
+
+ if (evsel->core.attr.sample_period) {
+ /* no sampling */
+ err = -EINVAL;
+ goto out_close;
+ }
+ if (pid > -1) {
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
+ fd = open(buf, O_RDONLY);
+ evsel->pid_stat = true;
+ } else {
+ fd = open("/proc/stat", O_RDONLY);
+ }
+ FD(evsel, idx, thread) = fd;
+ if (fd < 0) {
+ err = -errno;
+ goto out_close;
+ }
+ start_time = xyarray__entry(evsel->start_times, idx, thread);
+ if (pid > -1) {
+ err = read_pid_stat_field(fd, system ? 15 : 14,
+ start_time);
+ } else {
+ struct perf_cpu cpu;
+
+ cpu = perf_cpu_map__cpu(evsel->core.cpus, idx);
+ err = read_stat_field(fd, cpu, system ? 3 : 1,
+ start_time);
+ }
+ if (err)
+ goto out_close;
+ }
+
+ }
+ }
+ return 0;
+out_close:
+ if (err)
+ threads->err_thread = thread;
+
+ old_errno = errno;
+ do {
+ while (--thread >= 0) {
+ if (FD(evsel, idx, thread) >= 0)
+ close(FD(evsel, idx, thread));
+ FD(evsel, idx, thread) = -1;
+ }
+ thread = nthreads;
+ } while (--idx >= 0);
+ errno = old_errno;
+ return err;
+}
+
+int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
+{
+ __u64 *start_time, cur_time, delta_start;
+ int fd, err = 0;
+ struct perf_counts_values *count;
+ bool adjust = false;
+
+ count = perf_counts(evsel->counts, cpu_map_idx, thread);
+
+ switch (evsel__tool_event(evsel)) {
+ case PERF_TOOL_DURATION_TIME:
+ /*
+ * Pretend duration_time is only on the first CPU and thread, or
+ * else aggregation will scale duration_time by the number of
+ * CPUs/threads.
+ */
+ start_time = &evsel->start_time;
+ if (cpu_map_idx == 0 && thread == 0)
+ cur_time = rdclock();
+ else
+ cur_time = *start_time;
+ break;
+ case PERF_TOOL_USER_TIME:
+ case PERF_TOOL_SYSTEM_TIME: {
+ bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME;
+
+ start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread);
+ fd = FD(evsel, cpu_map_idx, thread);
+ lseek(fd, SEEK_SET, 0);
+ if (evsel->pid_stat) {
+ /* The event exists solely on 1 CPU. */
+ if (cpu_map_idx == 0)
+ err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time);
+ else
+ cur_time = 0;
+ } else {
+ /* The event is for all threads. */
+ if (thread == 0) {
+ struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus,
+ cpu_map_idx);
+
+ err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time);
+ } else {
+ cur_time = 0;
+ }
+ }
+ adjust = true;
+ break;
+ }
+ case PERF_TOOL_NONE:
+ case PERF_TOOL_MAX:
+ default:
+ err = -EINVAL;
+ }
+ if (err)
+ return err;
+
+ delta_start = cur_time - *start_time;
+ if (adjust) {
+ __u64 ticks_per_sec = sysconf(_SC_CLK_TCK);
+
+ delta_start *= 1000000000 / ticks_per_sec;
+ }
+ count->val = delta_start;
+ count->ena = count->run = delta_start;
+ count->lost = 0;
+ return 0;
+}
+
+struct perf_pmu *perf_pmus__tool_pmu(void)
+{
+ static struct perf_pmu tool = {
+ .name = "tool",
+ .type = PERF_PMU_TYPE_TOOL,
+ .aliases = LIST_HEAD_INIT(tool.aliases),
+ .caps = LIST_HEAD_INIT(tool.caps),
+ .format = LIST_HEAD_INIT(tool.format),
+ };
+
+ return &tool;
+}
diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h
new file mode 100644
index 000000000000..05a4052c8b9d
--- /dev/null
+++ b/tools/perf/util/tool_pmu.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __TOOL_PMU_H
+#define __TOOL_PMU_H
+
+#include "pmu.h"
+
+struct evsel;
+struct perf_thread_map;
+struct print_callbacks;
+
+enum perf_tool_event {
+ PERF_TOOL_NONE = 0,
+ PERF_TOOL_DURATION_TIME = 1,
+ PERF_TOOL_USER_TIME = 2,
+ PERF_TOOL_SYSTEM_TIME = 3,
+
+ PERF_TOOL_MAX,
+};
+
+#define perf_tool_event__for_each_event(ev) \
+ for ((ev) = PERF_TOOL_DURATION_TIME; (ev) < PERF_TOOL_MAX; ev++)
+
+static inline size_t tool_pmu__num_events(void)
+{
+ return PERF_TOOL_MAX - 1;
+}
+
+const char *perf_tool_event__to_str(enum perf_tool_event ev);
+enum perf_tool_event perf_tool_event__from_str(const char *str);
+int tool_pmu__config_terms(struct perf_event_attr *attr,
+ struct parse_events_terms *terms,
+ struct parse_events_error *err);
+int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb);
+
+bool perf_pmu__is_tool(const struct perf_pmu *pmu);
+
+
+bool evsel__is_tool(const struct evsel *evsel);
+enum perf_tool_event evsel__tool_event(const struct evsel *evsel);
+const char *evsel__tool_pmu_event_name(const struct evsel *evsel);
+int evsel__tool_pmu_prepare_open(struct evsel *evsel,
+ struct perf_cpu_map *cpus,
+ int nthreads);
+int evsel__tool_pmu_open(struct evsel *evsel,
+ struct perf_thread_map *threads,
+ int start_cpu_map_idx, int end_cpu_map_idx);
+int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread);
+
+struct perf_pmu *perf_pmus__tool_pmu(void);
+
+#endif /* __TOOL_PMU_H */
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 05/13] perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (3 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 04/13] perf tool_pmu: Factor tool events into their own PMU Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 06/13] perf tool_pmu: Rename perf_tool_event__* to tool_pmu__* Ian Rogers
` (8 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
To better reflect the events listed are from the tool PMU. Rename the
enum values from PERF_TOOL_* to TOOL_PMU__EVENT_*.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-stat.c | 6 ++---
tools/perf/util/evsel.c | 4 ++--
tools/perf/util/metricgroup.c | 12 +++++-----
tools/perf/util/pmu.c | 2 +-
tools/perf/util/stat-shadow.c | 10 ++++----
tools/perf/util/tool_pmu.c | 44 +++++++++++++++++------------------
tools/perf/util/tool_pmu.h | 22 +++++++++---------
7 files changed, 50 insertions(+), 50 deletions(-)
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index ae525fdcfbde..a6b4d1b98b3a 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -295,14 +295,14 @@ static int read_single_counter(struct evsel *counter, int cpu_map_idx, int threa
* terminates. Use the wait4 values in that case.
*/
if (err && cpu_map_idx == 0 &&
- (evsel__tool_event(counter) == PERF_TOOL_USER_TIME ||
- evsel__tool_event(counter) == PERF_TOOL_SYSTEM_TIME)) {
+ (evsel__tool_event(counter) == TOOL_PMU__EVENT_USER_TIME ||
+ evsel__tool_event(counter) == TOOL_PMU__EVENT_SYSTEM_TIME)) {
u64 val, *start_time;
struct perf_counts_values *count =
perf_counts(counter->counts, cpu_map_idx, thread);
start_time = xyarray__entry(counter->start_times, cpu_map_idx, thread);
- if (evsel__tool_event(counter) == PERF_TOOL_USER_TIME)
+ if (evsel__tool_event(counter) == TOOL_PMU__EVENT_USER_TIME)
val = ru_stats.ru_utime_usec_stat.mean;
else
val = ru_stats.ru_stime_usec_stat.mean;
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index b75c4a4c7aa7..c0b49c5dd3cf 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1471,8 +1471,8 @@ void evsel__exit(struct evsel *evsel)
evsel->per_pkg_mask = NULL;
zfree(&evsel->metric_events);
perf_evsel__object.fini(evsel);
- if (evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME ||
- evsel__tool_event(evsel) == PERF_TOOL_USER_TIME)
+ if (evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME ||
+ evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME)
xyarray__delete(evsel->start_times);
}
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 9181548e8881..0e21e2f9c170 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -674,20 +674,20 @@ static int metricgroup__build_event_string(struct strbuf *events,
struct hashmap_entry *cur;
size_t bkt;
bool no_group = true, has_tool_events = false;
- bool tool_events[PERF_TOOL_MAX] = {false};
+ bool tool_events[TOOL_PMU__EVENT_MAX] = {false};
int ret = 0;
#define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
hashmap__for_each_entry(ctx->ids, cur, bkt) {
const char *sep, *rsep, *id = cur->pkey;
- enum perf_tool_event ev;
+ enum tool_pmu_event ev;
pr_debug("found event %s\n", id);
/* Always move tool events outside of the group. */
ev = perf_tool_event__from_str(id);
- if (ev != PERF_TOOL_NONE) {
+ if (ev != TOOL_PMU__EVENT_NONE) {
has_tool_events = true;
tool_events[ev] = true;
continue;
@@ -1375,7 +1375,7 @@ static void metricgroup__free_metrics(struct list_head *metric_list)
* to true if tool event is found.
*/
static void find_tool_events(const struct list_head *metric_list,
- bool tool_events[PERF_TOOL_MAX])
+ bool tool_events[TOOL_PMU__EVENT_MAX])
{
struct metric *m;
@@ -1447,7 +1447,7 @@ static int build_combined_expr_ctx(const struct list_head *metric_list,
*/
static int parse_ids(bool metric_no_merge, bool fake_pmu,
struct expr_parse_ctx *ids, const char *modifier,
- bool group_events, const bool tool_events[PERF_TOOL_MAX],
+ bool group_events, const bool tool_events[TOOL_PMU__EVENT_MAX],
struct evlist **out_evlist)
{
struct parse_events_error parse_error;
@@ -1536,7 +1536,7 @@ static int parse_groups(struct evlist *perf_evlist,
struct evlist *combined_evlist = NULL;
LIST_HEAD(metric_list);
struct metric *m;
- bool tool_events[PERF_TOOL_MAX] = {false};
+ bool tool_events[TOOL_PMU__EVENT_MAX] = {false};
bool is_default = !strcmp(str, "Default");
int ret;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index f17e21455498..fadb705fd9a7 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1832,7 +1832,7 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
if (!name)
return false;
if (perf_pmu__is_tool(pmu))
- return perf_tool_event__from_str(name) != PERF_TOOL_NONE;
+ return perf_tool_event__from_str(name) != TOOL_PMU__EVENT_NONE;
if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
return true;
if (pmu->cpu_aliases_added || !pmu->events_table)
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index 2d653f91400e..e56969d1e39d 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -382,22 +382,22 @@ static int prepare_metric(const struct metric_expr *mexp,
double scale;
switch (evsel__tool_event(metric_events[i])) {
- case PERF_TOOL_DURATION_TIME:
+ case TOOL_PMU__EVENT_DURATION_TIME:
stats = &walltime_nsecs_stats;
scale = 1e-9;
break;
- case PERF_TOOL_USER_TIME:
+ case TOOL_PMU__EVENT_USER_TIME:
stats = &ru_stats.ru_utime_usec_stat;
scale = 1e-6;
break;
- case PERF_TOOL_SYSTEM_TIME:
+ case TOOL_PMU__EVENT_SYSTEM_TIME:
stats = &ru_stats.ru_stime_usec_stat;
scale = 1e-6;
break;
- case PERF_TOOL_NONE:
+ case TOOL_PMU__EVENT_NONE:
pr_err("Invalid tool event 'none'");
abort();
- case PERF_TOOL_MAX:
+ case TOOL_PMU__EVENT_MAX:
pr_err("Invalid tool event 'max'");
abort();
default:
diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c
index 702c1131a2a8..7ef559532f5b 100644
--- a/tools/perf/util/tool_pmu.c
+++ b/tools/perf/util/tool_pmu.c
@@ -12,7 +12,7 @@
#include <fcntl.h>
#include <strings.h>
-static const char *const tool_pmu__event_names[PERF_TOOL_MAX] = {
+static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] = {
NULL,
"duration_time",
"user_time",
@@ -20,15 +20,15 @@ static const char *const tool_pmu__event_names[PERF_TOOL_MAX] = {
};
-const char *perf_tool_event__to_str(enum perf_tool_event ev)
+const char *perf_tool_event__to_str(enum tool_pmu_event ev)
{
- if (ev > PERF_TOOL_NONE && ev < PERF_TOOL_MAX)
+ if (ev > TOOL_PMU__EVENT_NONE && ev < TOOL_PMU__EVENT_MAX)
return tool_pmu__event_names[ev];
return NULL;
}
-enum perf_tool_event perf_tool_event__from_str(const char *str)
+enum tool_pmu_event perf_tool_event__from_str(const char *str)
{
int i;
@@ -36,7 +36,7 @@ enum perf_tool_event perf_tool_event__from_str(const char *str)
if (!strcasecmp(str, tool_pmu__event_names[i]))
return i;
}
- return PERF_TOOL_NONE;
+ return TOOL_PMU__EVENT_NONE;
}
static int tool_pmu__config_term(struct perf_event_attr *attr,
@@ -44,9 +44,9 @@ static int tool_pmu__config_term(struct perf_event_attr *attr,
struct parse_events_error *err)
{
if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
- enum perf_tool_event ev = perf_tool_event__from_str(term->config);
+ enum tool_pmu_event ev = perf_tool_event__from_str(term->config);
- if (ev == PERF_TOOL_NONE)
+ if (ev == TOOL_PMU__EVENT_NONE)
goto err_out;
attr->config = ev;
@@ -120,12 +120,12 @@ bool evsel__is_tool(const struct evsel *evsel)
return perf_pmu__is_tool(evsel->pmu);
}
-enum perf_tool_event evsel__tool_event(const struct evsel *evsel)
+enum tool_pmu_event evsel__tool_event(const struct evsel *evsel)
{
if (!evsel__is_tool(evsel))
- return PERF_TOOL_NONE;
+ return TOOL_PMU__EVENT_NONE;
- return (enum perf_tool_event)evsel->core.attr.config;
+ return (enum tool_pmu_event)evsel->core.attr.config;
}
const char *evsel__tool_pmu_event_name(const struct evsel *evsel)
@@ -231,8 +231,8 @@ int evsel__tool_pmu_prepare_open(struct evsel *evsel,
struct perf_cpu_map *cpus,
int nthreads)
{
- if ((evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME ||
- evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) &&
+ if ((evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME ||
+ evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME) &&
!evsel->start_times) {
evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus),
nthreads,
@@ -249,10 +249,10 @@ int evsel__tool_pmu_open(struct evsel *evsel,
struct perf_thread_map *threads,
int start_cpu_map_idx, int end_cpu_map_idx)
{
- enum perf_tool_event ev = evsel__tool_event(evsel);
+ enum tool_pmu_event ev = evsel__tool_event(evsel);
int pid = -1, idx = 0, thread = 0, nthreads, err = 0, old_errno;
- if (ev == PERF_TOOL_DURATION_TIME) {
+ if (ev == TOOL_PMU__EVENT_DURATION_TIME) {
if (evsel->core.attr.sample_period) /* no sampling */
return -EINVAL;
evsel->start_time = rdclock();
@@ -271,8 +271,8 @@ int evsel__tool_pmu_open(struct evsel *evsel,
if (!evsel->cgrp && !evsel->core.system_wide)
pid = perf_thread_map__pid(threads, thread);
- if (ev == PERF_TOOL_USER_TIME || ev == PERF_TOOL_SYSTEM_TIME) {
- bool system = ev == PERF_TOOL_SYSTEM_TIME;
+ if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) {
+ bool system = ev == TOOL_PMU__EVENT_SYSTEM_TIME;
__u64 *start_time = NULL;
int fd;
@@ -340,7 +340,7 @@ int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
count = perf_counts(evsel->counts, cpu_map_idx, thread);
switch (evsel__tool_event(evsel)) {
- case PERF_TOOL_DURATION_TIME:
+ case TOOL_PMU__EVENT_DURATION_TIME:
/*
* Pretend duration_time is only on the first CPU and thread, or
* else aggregation will scale duration_time by the number of
@@ -352,9 +352,9 @@ int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
else
cur_time = *start_time;
break;
- case PERF_TOOL_USER_TIME:
- case PERF_TOOL_SYSTEM_TIME: {
- bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME;
+ case TOOL_PMU__EVENT_USER_TIME:
+ case TOOL_PMU__EVENT_SYSTEM_TIME: {
+ bool system = evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME;
start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread);
fd = FD(evsel, cpu_map_idx, thread);
@@ -379,8 +379,8 @@ int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
adjust = true;
break;
}
- case PERF_TOOL_NONE:
- case PERF_TOOL_MAX:
+ case TOOL_PMU__EVENT_NONE:
+ case TOOL_PMU__EVENT_MAX:
default:
err = -EINVAL;
}
diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h
index 05a4052c8b9d..b27a26133927 100644
--- a/tools/perf/util/tool_pmu.h
+++ b/tools/perf/util/tool_pmu.h
@@ -8,25 +8,25 @@ struct evsel;
struct perf_thread_map;
struct print_callbacks;
-enum perf_tool_event {
- PERF_TOOL_NONE = 0,
- PERF_TOOL_DURATION_TIME = 1,
- PERF_TOOL_USER_TIME = 2,
- PERF_TOOL_SYSTEM_TIME = 3,
+enum tool_pmu_event {
+ TOOL_PMU__EVENT_NONE = 0,
+ TOOL_PMU__EVENT_DURATION_TIME = 1,
+ TOOL_PMU__EVENT_USER_TIME = 2,
+ TOOL_PMU__EVENT_SYSTEM_TIME = 3,
- PERF_TOOL_MAX,
+ TOOL_PMU__EVENT_MAX,
};
#define perf_tool_event__for_each_event(ev) \
- for ((ev) = PERF_TOOL_DURATION_TIME; (ev) < PERF_TOOL_MAX; ev++)
+ for ((ev) = TOOL_PMU__EVENT_DURATION_TIME; (ev) < TOOL_PMU__EVENT_MAX; ev++)
static inline size_t tool_pmu__num_events(void)
{
- return PERF_TOOL_MAX - 1;
+ return TOOL_PMU__EVENT_MAX - 1;
}
-const char *perf_tool_event__to_str(enum perf_tool_event ev);
-enum perf_tool_event perf_tool_event__from_str(const char *str);
+const char *perf_tool_event__to_str(enum tool_pmu_event ev);
+enum tool_pmu_event perf_tool_event__from_str(const char *str);
int tool_pmu__config_terms(struct perf_event_attr *attr,
struct parse_events_terms *terms,
struct parse_events_error *err);
@@ -36,7 +36,7 @@ bool perf_pmu__is_tool(const struct perf_pmu *pmu);
bool evsel__is_tool(const struct evsel *evsel);
-enum perf_tool_event evsel__tool_event(const struct evsel *evsel);
+enum tool_pmu_event evsel__tool_event(const struct evsel *evsel);
const char *evsel__tool_pmu_event_name(const struct evsel *evsel);
int evsel__tool_pmu_prepare_open(struct evsel *evsel,
struct perf_cpu_map *cpus,
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 06/13] perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (4 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 05/13] perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 07/13] perf tool_pmu: Move expr literals to tool_pmu Ian Rogers
` (7 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Now the events are associated with the tool PMU, rename the functions
to reflect this.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/evsel.c | 2 +-
tools/perf/util/metricgroup.c | 22 +++++++++++-----------
tools/perf/util/pmu.c | 4 ++--
tools/perf/util/tool_pmu.c | 16 ++++++++--------
tools/perf/util/tool_pmu.h | 8 ++++----
5 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index c0b49c5dd3cf..9e748ed20988 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1655,7 +1655,7 @@ static int evsel__read_group(struct evsel *leader, int cpu_map_idx, int thread)
int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread)
{
if (evsel__is_tool(evsel))
- return evsel__read_tool(evsel, cpu_map_idx, thread);
+ return evsel__tool_pmu_read(evsel, cpu_map_idx, thread);
if (evsel__is_retire_lat(evsel))
return evsel__read_retire_lat(evsel, cpu_map_idx, thread);
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 0e21e2f9c170..e5dd932e4d3f 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -686,7 +686,7 @@ static int metricgroup__build_event_string(struct strbuf *events,
pr_debug("found event %s\n", id);
/* Always move tool events outside of the group. */
- ev = perf_tool_event__from_str(id);
+ ev = tool_pmu__str_to_event(id);
if (ev != TOOL_PMU__EVENT_NONE) {
has_tool_events = true;
tool_events[ev] = true;
@@ -755,14 +755,14 @@ static int metricgroup__build_event_string(struct strbuf *events,
if (has_tool_events) {
int i;
- perf_tool_event__for_each_event(i) {
+ tool_pmu__for_each_event(i) {
if (tool_events[i]) {
if (!no_group) {
ret = strbuf_addch(events, ',');
RETURN_IF_NON_ZERO(ret);
}
no_group = false;
- ret = strbuf_addstr(events, perf_tool_event__to_str(i));
+ ret = strbuf_addstr(events, tool_pmu__event_to_str(i));
RETURN_IF_NON_ZERO(ret);
}
}
@@ -1148,14 +1148,14 @@ static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
int i, left_count, right_count;
left_count = hashmap__size(left->pctx->ids);
- perf_tool_event__for_each_event(i) {
- if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data))
+ tool_pmu__for_each_event(i) {
+ if (!expr__get_id(left->pctx, tool_pmu__event_to_str(i), &data))
left_count--;
}
right_count = hashmap__size(right->pctx->ids);
- perf_tool_event__for_each_event(i) {
- if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data))
+ tool_pmu__for_each_event(i) {
+ if (!expr__get_id(right->pctx, tool_pmu__event_to_str(i), &data))
right_count--;
}
@@ -1382,11 +1382,11 @@ static void find_tool_events(const struct list_head *metric_list,
list_for_each_entry(m, metric_list, nd) {
int i;
- perf_tool_event__for_each_event(i) {
+ tool_pmu__for_each_event(i) {
struct expr_id_data *data;
if (!tool_events[i] &&
- !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data))
+ !expr__get_id(m->pctx, tool_pmu__event_to_str(i), &data))
tool_events[i] = true;
}
}
@@ -1472,9 +1472,9 @@ static int parse_ids(bool metric_no_merge, bool fake_pmu,
* event1 if #smt_on else 0
* Add a tool event to avoid a parse error on an empty string.
*/
- perf_tool_event__for_each_event(i) {
+ tool_pmu__for_each_event(i) {
if (tool_events[i]) {
- char *tmp = strdup(perf_tool_event__to_str(i));
+ char *tmp = strdup(tool_pmu__event_to_str(i));
if (!tmp)
return -ENOMEM;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index fadb705fd9a7..d6310fa1b8bd 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1832,7 +1832,7 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
if (!name)
return false;
if (perf_pmu__is_tool(pmu))
- return perf_tool_event__from_str(name) != TOOL_PMU__EVENT_NONE;
+ return tool_pmu__str_to_event(name) != TOOL_PMU__EVENT_NONE;
if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
return true;
if (pmu->cpu_aliases_added || !pmu->events_table)
@@ -2327,7 +2327,7 @@ const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config)
return NULL;
if (perf_pmu__is_tool(pmu))
- return perf_tool_event__to_str(config);
+ return tool_pmu__event_to_str(config);
pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu);
diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c
index 7ef559532f5b..b8116c5c7250 100644
--- a/tools/perf/util/tool_pmu.c
+++ b/tools/perf/util/tool_pmu.c
@@ -20,7 +20,7 @@ static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] = {
};
-const char *perf_tool_event__to_str(enum tool_pmu_event ev)
+const char *tool_pmu__event_to_str(enum tool_pmu_event ev)
{
if (ev > TOOL_PMU__EVENT_NONE && ev < TOOL_PMU__EVENT_MAX)
return tool_pmu__event_names[ev];
@@ -28,11 +28,11 @@ const char *perf_tool_event__to_str(enum tool_pmu_event ev)
return NULL;
}
-enum tool_pmu_event perf_tool_event__from_str(const char *str)
+enum tool_pmu_event tool_pmu__str_to_event(const char *str)
{
int i;
- perf_tool_event__for_each_event(i) {
+ tool_pmu__for_each_event(i) {
if (!strcasecmp(str, tool_pmu__event_names[i]))
return i;
}
@@ -44,7 +44,7 @@ static int tool_pmu__config_term(struct perf_event_attr *attr,
struct parse_events_error *err)
{
if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
- enum tool_pmu_event ev = perf_tool_event__from_str(term->config);
+ enum tool_pmu_event ev = tool_pmu__str_to_event(term->config);
if (ev == TOOL_PMU__EVENT_NONE)
goto err_out;
@@ -91,10 +91,10 @@ int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_cal
};
int i;
- perf_tool_event__for_each_event(i) {
+ tool_pmu__for_each_event(i) {
int ret;
- info.name = perf_tool_event__to_str(i);
+ info.name = tool_pmu__event_to_str(i);
info.alias = NULL;
info.scale_unit = NULL;
info.desc = NULL;
@@ -130,7 +130,7 @@ enum tool_pmu_event evsel__tool_event(const struct evsel *evsel)
const char *evsel__tool_pmu_event_name(const struct evsel *evsel)
{
- return perf_tool_event__to_str(evsel->core.attr.config);
+ return tool_pmu__event_to_str(evsel->core.attr.config);
}
static bool read_until_char(struct io *io, char e)
@@ -330,7 +330,7 @@ int evsel__tool_pmu_open(struct evsel *evsel,
return err;
}
-int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
+int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread)
{
__u64 *start_time, cur_time, delta_start;
int fd, err = 0;
diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h
index b27a26133927..b156645206c4 100644
--- a/tools/perf/util/tool_pmu.h
+++ b/tools/perf/util/tool_pmu.h
@@ -17,7 +17,7 @@ enum tool_pmu_event {
TOOL_PMU__EVENT_MAX,
};
-#define perf_tool_event__for_each_event(ev) \
+#define tool_pmu__for_each_event(ev) \
for ((ev) = TOOL_PMU__EVENT_DURATION_TIME; (ev) < TOOL_PMU__EVENT_MAX; ev++)
static inline size_t tool_pmu__num_events(void)
@@ -25,8 +25,8 @@ static inline size_t tool_pmu__num_events(void)
return TOOL_PMU__EVENT_MAX - 1;
}
-const char *perf_tool_event__to_str(enum tool_pmu_event ev);
-enum tool_pmu_event perf_tool_event__from_str(const char *str);
+const char *tool_pmu__event_to_str(enum tool_pmu_event ev);
+enum tool_pmu_event tool_pmu__str_to_event(const char *str);
int tool_pmu__config_terms(struct perf_event_attr *attr,
struct parse_events_terms *terms,
struct parse_events_error *err);
@@ -44,7 +44,7 @@ int evsel__tool_pmu_prepare_open(struct evsel *evsel,
int evsel__tool_pmu_open(struct evsel *evsel,
struct perf_thread_map *threads,
int start_cpu_map_idx, int end_cpu_map_idx);
-int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread);
+int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread);
struct perf_pmu *perf_pmus__tool_pmu(void);
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 07/13] perf tool_pmu: Move expr literals to tool_pmu
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (5 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 06/13] perf tool_pmu: Rename perf_tool_event__* to tool_pmu__* Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 08/13] perf jevents: Add tool event json under a common architecture Ian Rogers
` (6 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Add the expr literals like "#smt_on" as tool events, this allows stat
events to give the values. On my laptop with hyperthreading enabled:
```
$ perf stat -e "has_pmem,num_cores,num_cpus,num_cpus_online,num_dies,num_packages,smt_on,system_tsc_freq" true
Performance counter stats for 'true':
0 has_pmem
8 num_cores
16 num_cpus
16 num_cpus_online
1 num_dies
1 num_packages
1 smt_on
2,496,000,000 system_tsc_freq
0.001113637 seconds time elapsed
0.001218000 seconds user
0.000000000 seconds sys
```
And with hyperthreading disabled:
```
$ perf stat -e "has_pmem,num_cores,num_cpus,num_cpus_online,num_dies,num_packages,smt_on,system_tsc_freq" true
Performance counter stats for 'true':
0 has_pmem
8 num_cores
16 num_cpus
8 num_cpus_online
1 num_dies
1 num_packages
0 smt_on
2,496,000,000 system_tsc_freq
0.000802115 seconds time elapsed
0.000000000 seconds user
0.000806000 seconds sys
```
As zero matters for these values, in stat-display
should_skip_zero_counter only skip the zero value if it is not the
first aggregation index.
The tool event implementations are used in expr but not evaluated as
events for simplicity. Also core_wide isn't made a tool event as it
requires command line parameters.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/arm64/util/pmu.c | 5 +-
tools/perf/arch/x86/util/tsc.c | 18 ++--
tools/perf/util/expr.c | 93 +++-----------------
tools/perf/util/pmu.c | 5 --
tools/perf/util/pmu.h | 1 -
tools/perf/util/stat-display.c | 8 +-
tools/perf/util/stat-shadow.c | 11 ++-
tools/perf/util/tool_pmu.c | 146 ++++++++++++++++++++++++++++++-
tools/perf/util/tool_pmu.h | 19 +++-
tools/perf/util/tsc.h | 2 +-
10 files changed, 201 insertions(+), 107 deletions(-)
diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c
index 2a4eab2d160e..a0964b191fcb 100644
--- a/tools/perf/arch/arm64/util/pmu.c
+++ b/tools/perf/arch/arm64/util/pmu.c
@@ -5,6 +5,7 @@
#include "../../../util/header.h"
#include "../../../util/pmu.h"
#include "../../../util/pmus.h"
+#include "../../../util/tool_pmu.h"
#include <api/fs/fs.h>
#include <math.h>
@@ -24,7 +25,7 @@ const struct pmu_metrics_table *pmu_metrics_table__find(void)
return NULL;
}
-double perf_pmu__cpu_slots_per_cycle(void)
+u64 tool_pmu__cpu_slots_per_cycle(void)
{
char path[PATH_MAX];
unsigned long long slots = 0;
@@ -41,5 +42,5 @@ double perf_pmu__cpu_slots_per_cycle(void)
filename__read_ull(path, &slots);
}
- return slots ? (double)slots : NAN;
+ return slots;
}
diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c
index e2d6cfe21057..3a439e4b12d2 100644
--- a/tools/perf/arch/x86/util/tsc.c
+++ b/tools/perf/arch/x86/util/tsc.c
@@ -24,9 +24,9 @@ u64 rdtsc(void)
* ...
* will return 3000000000.
*/
-static double cpuinfo_tsc_freq(void)
+static u64 cpuinfo_tsc_freq(void)
{
- double result = 0;
+ u64 result = 0;
FILE *cpuinfo;
char *line = NULL;
size_t len = 0;
@@ -34,20 +34,22 @@ static double cpuinfo_tsc_freq(void)
cpuinfo = fopen("/proc/cpuinfo", "r");
if (!cpuinfo) {
pr_err("Failed to read /proc/cpuinfo for TSC frequency\n");
- return NAN;
+ return 0;
}
while (getline(&line, &len, cpuinfo) > 0) {
if (!strncmp(line, "model name", 10)) {
char *pos = strstr(line + 11, " @ ");
+ double float_result;
- if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) {
- result *= 1000000000;
+ if (pos && sscanf(pos, " @ %lfGHz", &float_result) == 1) {
+ float_result *= 1000000000;
+ result = (u64)float_result;
goto out;
}
}
}
out:
- if (fpclassify(result) == FP_ZERO)
+ if (result == 0)
pr_err("Failed to find TSC frequency in /proc/cpuinfo\n");
free(line);
@@ -55,7 +57,7 @@ static double cpuinfo_tsc_freq(void)
return result;
}
-double arch_get_tsc_freq(void)
+u64 arch_get_tsc_freq(void)
{
unsigned int a, b, c, d, lvl;
static bool cached;
@@ -86,6 +88,6 @@ double arch_get_tsc_freq(void)
return tsc;
}
- tsc = (double)c * (double)b / (double)a;
+ tsc = (u64)c * (u64)b / (u64)a;
return tsc;
}
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index b2536a59c44e..5e3732bc2fa5 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -5,25 +5,22 @@
#include <stdlib.h>
#include <string.h>
#include "metricgroup.h"
-#include "cpumap.h"
-#include "cputopo.h"
#include "debug.h"
#include "evlist.h"
#include "expr.h"
+#include "pmu.h"
+#include "smt.h"
+#include "tool_pmu.h"
#include <util/expr-bison.h>
#include <util/expr-flex.h>
#include "util/hashmap.h"
#include "util/header.h"
#include "util/pmu.h"
-#include "smt.h"
-#include "tsc.h"
-#include <api/fs/fs.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/zalloc.h>
#include <ctype.h>
#include <math.h>
-#include "pmu.h"
struct expr_id_data {
union {
@@ -393,90 +390,26 @@ double expr_id_data__source_count(const struct expr_id_data *data)
return data->val.source_count;
}
-#if !defined(__i386__) && !defined(__x86_64__)
-double arch_get_tsc_freq(void)
-{
- return 0.0;
-}
-#endif
-
-static double has_pmem(void)
-{
- static bool has_pmem, cached;
- const char *sysfs = sysfs__mountpoint();
- char path[PATH_MAX];
-
- if (!cached) {
- snprintf(path, sizeof(path), "%s/firmware/acpi/tables/NFIT", sysfs);
- has_pmem = access(path, F_OK) == 0;
- cached = true;
- }
- return has_pmem ? 1.0 : 0.0;
-}
-
double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx)
{
- const struct cpu_topology *topology;
double result = NAN;
+ enum tool_pmu_event ev = tool_pmu__str_to_event(literal + 1);
- if (!strcmp("#num_cpus", literal)) {
- result = cpu__max_present_cpu().cpu;
- goto out;
- }
- if (!strcmp("#num_cpus_online", literal)) {
- struct perf_cpu_map *online = cpu_map__online();
-
- if (online)
- result = perf_cpu_map__nr(online);
- goto out;
- }
+ if (ev != TOOL_PMU__EVENT_NONE) {
+ u64 count;
- if (!strcasecmp("#system_tsc_freq", literal)) {
- result = arch_get_tsc_freq();
- goto out;
- }
+ if (tool_pmu__read_event(ev, &count))
+ result = count;
+ else
+ pr_err("Failure to read '%s'", literal);
- /*
- * Assume that topology strings are consistent, such as CPUs "0-1"
- * wouldn't be listed as "0,1", and so after deduplication the number of
- * these strings gives an indication of the number of packages, dies,
- * etc.
- */
- if (!strcasecmp("#smt_on", literal)) {
- result = smt_on() ? 1.0 : 0.0;
- goto out;
- }
- if (!strcmp("#core_wide", literal)) {
+ } else if (!strcmp("#core_wide", literal)) {
result = core_wide(ctx->system_wide, ctx->user_requested_cpu_list)
? 1.0 : 0.0;
- goto out;
- }
- if (!strcmp("#num_packages", literal)) {
- topology = online_topology();
- result = topology->package_cpus_lists;
- goto out;
- }
- if (!strcmp("#num_dies", literal)) {
- topology = online_topology();
- result = topology->die_cpus_lists;
- goto out;
- }
- if (!strcmp("#num_cores", literal)) {
- topology = online_topology();
- result = topology->core_cpus_lists;
- goto out;
- }
- if (!strcmp("#slots", literal)) {
- result = perf_pmu__cpu_slots_per_cycle();
- goto out;
- }
- if (!strcmp("#has_pmem", literal)) {
- result = has_pmem();
- goto out;
+ } else {
+ pr_err("Unrecognized literal '%s'", literal);
}
- pr_err("Unrecognized literal '%s'", literal);
-out:
pr_debug2("literal: %s = %f\n", literal, result);
return result;
}
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index d6310fa1b8bd..9cb7a4e5e7d4 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -2250,11 +2250,6 @@ bool perf_pmu__match(const struct perf_pmu *pmu, const char *tok)
(need_fnmatch && !fnmatch(tok, name, 0));
}
-double __weak perf_pmu__cpu_slots_per_cycle(void)
-{
- return NAN;
-}
-
int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size)
{
const char *sysfs = sysfs__mountpoint();
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 880a8dc661b7..c4ca359d4215 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -271,7 +271,6 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu);
bool perf_pmu__match(const struct perf_pmu *pmu, const char *tok);
-double perf_pmu__cpu_slots_per_cycle(void);
int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size);
int perf_pmu__pathname_scnprintf(char *buf, size_t size,
const char *pmu_name, const char *filename);
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index a82a8ec79b39..ad8baf33fdba 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -951,8 +951,12 @@ static bool should_skip_zero_counter(struct perf_stat_config *config,
* Many tool events are only gathered on the first index, skip other
* zero values.
*/
- if (evsel__is_tool(counter))
- return true;
+ if (evsel__is_tool(counter)) {
+ struct aggr_cpu_id own_id =
+ config->aggr_get_id(config, (struct perf_cpu){ .cpu = 0 });
+
+ return !aggr_cpu_id__equal(id, &own_id);
+ }
/*
* Skip value 0 when it's an uncore event and the given aggr id
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index e56969d1e39d..aa367c667926 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -400,8 +400,17 @@ static int prepare_metric(const struct metric_expr *mexp,
case TOOL_PMU__EVENT_MAX:
pr_err("Invalid tool event 'max'");
abort();
+ case TOOL_PMU__EVENT_HAS_PMEM:
+ case TOOL_PMU__EVENT_NUM_CORES:
+ case TOOL_PMU__EVENT_NUM_CPUS:
+ case TOOL_PMU__EVENT_NUM_CPUS_ONLINE:
+ case TOOL_PMU__EVENT_NUM_DIES:
+ case TOOL_PMU__EVENT_NUM_PACKAGES:
+ case TOOL_PMU__EVENT_SLOTS:
+ case TOOL_PMU__EVENT_SMT_ON:
+ case TOOL_PMU__EVENT_SYSTEM_TSC_FREQ:
default:
- pr_err("Unknown tool event '%s'", evsel__name(metric_events[i]));
+ pr_err("Unexpected tool event '%s'", evsel__name(metric_events[i]));
abort();
}
val = avg_stats(stats) * scale;
diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c
index b8116c5c7250..3768c6a42ff6 100644
--- a/tools/perf/util/tool_pmu.c
+++ b/tools/perf/util/tool_pmu.c
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "cgroup.h"
#include "counts.h"
+#include "cputopo.h"
#include "evsel.h"
#include "pmu.h"
#include "print-events.h"
+#include "smt.h"
#include "time-utils.h"
#include "tool_pmu.h"
+#include "tsc.h"
+#include <api/fs/fs.h>
+#include <api/io.h>
#include <api/io.h>
#include <internal/threadmap.h>
#include <perf/threadmap.h>
@@ -17,6 +22,15 @@ static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] = {
"duration_time",
"user_time",
"system_time",
+ "has_pmem",
+ "num_cores",
+ "num_cpus",
+ "num_cpus_online",
+ "num_dies",
+ "num_packages",
+ "slots",
+ "smt_on",
+ "system_tsc_freq",
};
@@ -33,8 +47,14 @@ enum tool_pmu_event tool_pmu__str_to_event(const char *str)
int i;
tool_pmu__for_each_event(i) {
- if (!strcasecmp(str, tool_pmu__event_names[i]))
+ if (!strcasecmp(str, tool_pmu__event_names[i])) {
+#if !defined(__aarch64__)
+ /* The slots event should only appear on arm64. */
+ if (i == TOOL_PMU__EVENT_SLOTS)
+ return TOOL_PMU__EVENT_NONE;
+#endif
return i;
+ }
}
return TOOL_PMU__EVENT_NONE;
}
@@ -252,6 +272,9 @@ int evsel__tool_pmu_open(struct evsel *evsel,
enum tool_pmu_event ev = evsel__tool_event(evsel);
int pid = -1, idx = 0, thread = 0, nthreads, err = 0, old_errno;
+ if (ev == TOOL_PMU__EVENT_NUM_CPUS)
+ return 0;
+
if (ev == TOOL_PMU__EVENT_DURATION_TIME) {
if (evsel->core.attr.sample_period) /* no sampling */
return -EINVAL;
@@ -330,16 +353,133 @@ int evsel__tool_pmu_open(struct evsel *evsel,
return err;
}
+#if !defined(__i386__) && !defined(__x86_64__)
+u64 arch_get_tsc_freq(void)
+{
+ return 0;
+}
+#endif
+
+#if !defined(__aarch64__)
+u64 tool_pmu__cpu_slots_per_cycle(void)
+{
+ return 0;
+}
+#endif
+
+static bool has_pmem(void)
+{
+ static bool has_pmem, cached;
+ const char *sysfs = sysfs__mountpoint();
+ char path[PATH_MAX];
+
+ if (!cached) {
+ snprintf(path, sizeof(path), "%s/firmware/acpi/tables/NFIT", sysfs);
+ has_pmem = access(path, F_OK) == 0;
+ cached = true;
+ }
+ return has_pmem;
+}
+
+bool tool_pmu__read_event(enum tool_pmu_event ev, u64 *result)
+{
+ const struct cpu_topology *topology;
+
+ switch (ev) {
+ case TOOL_PMU__EVENT_HAS_PMEM:
+ *result = has_pmem() ? 1 : 0;
+ return true;
+
+ case TOOL_PMU__EVENT_NUM_CORES:
+ topology = online_topology();
+ *result = topology->core_cpus_lists;
+ return true;
+
+ case TOOL_PMU__EVENT_NUM_CPUS:
+ *result = cpu__max_present_cpu().cpu;
+ return true;
+
+ case TOOL_PMU__EVENT_NUM_CPUS_ONLINE: {
+ struct perf_cpu_map *online = cpu_map__online();
+
+ if (online) {
+ *result = perf_cpu_map__nr(online);
+ return true;
+ }
+ return false;
+ }
+ case TOOL_PMU__EVENT_NUM_DIES:
+ topology = online_topology();
+ *result = topology->die_cpus_lists;
+ return true;
+
+ case TOOL_PMU__EVENT_NUM_PACKAGES:
+ topology = online_topology();
+ *result = topology->package_cpus_lists;
+ return true;
+
+ case TOOL_PMU__EVENT_SLOTS:
+ *result = tool_pmu__cpu_slots_per_cycle();
+ return *result ? true : false;
+
+ case TOOL_PMU__EVENT_SMT_ON:
+ *result = smt_on() ? 1 : 0;
+ return true;
+
+ case TOOL_PMU__EVENT_SYSTEM_TSC_FREQ:
+ *result = arch_get_tsc_freq();
+ return true;
+
+ case TOOL_PMU__EVENT_NONE:
+ case TOOL_PMU__EVENT_DURATION_TIME:
+ case TOOL_PMU__EVENT_USER_TIME:
+ case TOOL_PMU__EVENT_SYSTEM_TIME:
+ case TOOL_PMU__EVENT_MAX:
+ default:
+ return false;
+ }
+}
+
int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread)
{
__u64 *start_time, cur_time, delta_start;
+ unsigned long val;
int fd, err = 0;
- struct perf_counts_values *count;
+ struct perf_counts_values *count, *old_count = NULL;
bool adjust = false;
+ enum tool_pmu_event ev = evsel__tool_event(evsel);
count = perf_counts(evsel->counts, cpu_map_idx, thread);
- switch (evsel__tool_event(evsel)) {
+ switch (ev) {
+ case TOOL_PMU__EVENT_HAS_PMEM:
+ case TOOL_PMU__EVENT_NUM_CORES:
+ case TOOL_PMU__EVENT_NUM_CPUS:
+ case TOOL_PMU__EVENT_NUM_CPUS_ONLINE:
+ case TOOL_PMU__EVENT_NUM_DIES:
+ case TOOL_PMU__EVENT_NUM_PACKAGES:
+ case TOOL_PMU__EVENT_SLOTS:
+ case TOOL_PMU__EVENT_SMT_ON:
+ case TOOL_PMU__EVENT_SYSTEM_TSC_FREQ:
+ if (evsel->prev_raw_counts)
+ old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread);
+ val = 0;
+ if (cpu_map_idx == 0 && thread == 0) {
+ if (!tool_pmu__read_event(ev, &val)) {
+ count->lost++;
+ val = 0;
+ }
+ }
+ if (old_count) {
+ count->val = old_count->val + val;
+ count->run = old_count->run + 1;
+ count->ena = old_count->ena + 1;
+ } else {
+ count->val = val;
+ count->run++;
+ count->ena++;
+ }
+ return 0;
case TOOL_PMU__EVENT_DURATION_TIME:
/*
* Pretend duration_time is only on the first CPU and thread, or
diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h
index b156645206c4..ecdf316525bb 100644
--- a/tools/perf/util/tool_pmu.h
+++ b/tools/perf/util/tool_pmu.h
@@ -10,9 +10,18 @@ struct print_callbacks;
enum tool_pmu_event {
TOOL_PMU__EVENT_NONE = 0,
- TOOL_PMU__EVENT_DURATION_TIME = 1,
- TOOL_PMU__EVENT_USER_TIME = 2,
- TOOL_PMU__EVENT_SYSTEM_TIME = 3,
+ TOOL_PMU__EVENT_DURATION_TIME,
+ TOOL_PMU__EVENT_USER_TIME,
+ TOOL_PMU__EVENT_SYSTEM_TIME,
+ TOOL_PMU__EVENT_HAS_PMEM,
+ TOOL_PMU__EVENT_NUM_CORES,
+ TOOL_PMU__EVENT_NUM_CPUS,
+ TOOL_PMU__EVENT_NUM_CPUS_ONLINE,
+ TOOL_PMU__EVENT_NUM_DIES,
+ TOOL_PMU__EVENT_NUM_PACKAGES,
+ TOOL_PMU__EVENT_SLOTS,
+ TOOL_PMU__EVENT_SMT_ON,
+ TOOL_PMU__EVENT_SYSTEM_TSC_FREQ,
TOOL_PMU__EVENT_MAX,
};
@@ -31,9 +40,11 @@ int tool_pmu__config_terms(struct perf_event_attr *attr,
struct parse_events_terms *terms,
struct parse_events_error *err);
int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb);
+bool tool_pmu__read_event(enum tool_pmu_event ev, u64 *result);
-bool perf_pmu__is_tool(const struct perf_pmu *pmu);
+u64 tool_pmu__cpu_slots_per_cycle(void);
+bool perf_pmu__is_tool(const struct perf_pmu *pmu);
bool evsel__is_tool(const struct evsel *evsel);
enum tool_pmu_event evsel__tool_event(const struct evsel *evsel);
diff --git a/tools/perf/util/tsc.h b/tools/perf/util/tsc.h
index 88fd1c4c1cb8..57ce8449647f 100644
--- a/tools/perf/util/tsc.h
+++ b/tools/perf/util/tsc.h
@@ -25,7 +25,7 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);
u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
u64 rdtsc(void);
-double arch_get_tsc_freq(void);
+u64 arch_get_tsc_freq(void);
size_t perf_event__fprintf_time_conv(union perf_event *event, FILE *fp);
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 08/13] perf jevents: Add tool event json under a common architecture
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (6 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 07/13] perf tool_pmu: Move expr literals to tool_pmu Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 09/13] perf tool_pmu: Switch to standard pmu functions and json descriptions Ian Rogers
` (5 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Introduce the notion of a common architecture/model that can be used
to find event tables for common PMUs like the tool PMU. By having tool
events be json standard PMU attribute configuration, descriptions,
etc. can be used and these routines are already optimized for things
like binary searching.
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../pmu-events/arch/common/common/tool.json | 74 +++++++
tools/perf/pmu-events/empty-pmu-events.c | 208 +++++++++++-------
tools/perf/pmu-events/jevents.py | 16 +-
3 files changed, 215 insertions(+), 83 deletions(-)
create mode 100644 tools/perf/pmu-events/arch/common/common/tool.json
diff --git a/tools/perf/pmu-events/arch/common/common/tool.json b/tools/perf/pmu-events/arch/common/common/tool.json
new file mode 100644
index 000000000000..12f2ef1813a6
--- /dev/null
+++ b/tools/perf/pmu-events/arch/common/common/tool.json
@@ -0,0 +1,74 @@
+[
+ {
+ "Unit": "tool",
+ "EventName": "duration_time",
+ "BriefDescription": "Wall clock interval time in nanoseconds",
+ "ConfigCode": "1"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "user_time",
+ "BriefDescription": "User (non-kernel) time in nanoseconds",
+ "ConfigCode": "2"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "system_time",
+ "BriefDescription": "System/kernel time in nanoseconds",
+ "ConfigCode": "3"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "has_pmem",
+ "BriefDescription": "1 if persistent memory installed otherwise 0",
+ "ConfigCode": "4"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "num_cores",
+ "BriefDescription": "Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU",
+ "ConfigCode": "5"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "num_cpus",
+ "BriefDescription": "Number of logical Linux CPUs. There may be multiple such CPUs on a core",
+ "ConfigCode": "6"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "num_cpus_online",
+ "BriefDescription": "Number of online logical Linux CPUs. There may be multiple such CPUs on a core",
+ "ConfigCode": "7"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "num_dies",
+ "BriefDescription": "Number of dies. Each die has 1 or more cores",
+ "ConfigCode": "8"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "num_packages",
+ "BriefDescription": "Number of packages. Each package has 1 or more die",
+ "ConfigCode": "9"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "slots",
+ "BriefDescription": "Number of functional units that in parallel can execute parts of an instruction",
+ "ConfigCode": "10"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "smt_on",
+ "BriefDescription": "1 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0",
+ "ConfigCode": "11"
+ },
+ {
+ "Unit": "tool",
+ "EventName": "system_tsc_freq",
+ "BriefDescription": "The amount a Time Stamp Counter (TSC) increases per second",
+ "ConfigCode": "12"
+ }
+]
diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index c592079982fb..47ed8b03e445 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -19,72 +19,109 @@ struct pmu_table_entry {
};
static const char *const big_c_string =
-/* offset=0 */ "default_core\000"
-/* offset=13 */ "bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000"
-/* offset=72 */ "bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000"
-/* offset=131 */ "l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000"
-/* offset=226 */ "segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000"
-/* offset=325 */ "dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000"
-/* offset=455 */ "eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000"
-/* offset=570 */ "hisi_sccl,ddrc\000"
-/* offset=585 */ "uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000"
-/* offset=671 */ "uncore_cbox\000"
-/* offset=683 */ "unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000"
-/* offset=914 */ "event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000"
-/* offset=979 */ "event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000"
-/* offset=1050 */ "hisi_sccl,l3c\000"
-/* offset=1064 */ "uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000"
-/* offset=1144 */ "uncore_imc_free_running\000"
-/* offset=1168 */ "uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000"
-/* offset=1263 */ "uncore_imc\000"
-/* offset=1274 */ "uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000"
-/* offset=1352 */ "uncore_sys_ddr_pmu\000"
-/* offset=1371 */ "sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000"
-/* offset=1444 */ "uncore_sys_ccn_pmu\000"
-/* offset=1463 */ "sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000"
-/* offset=1537 */ "uncore_sys_cmn_pmu\000"
-/* offset=1556 */ "sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000"
-/* offset=1696 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000"
-/* offset=1718 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000"
-/* offset=1781 */ "Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000"
-/* offset=1947 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000"
-/* offset=2011 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000"
-/* offset=2078 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000"
-/* offset=2149 */ "DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000"
-/* offset=2243 */ "DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000"
-/* offset=2377 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000"
-/* offset=2441 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000"
-/* offset=2509 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000"
-/* offset=2579 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\00000"
-/* offset=2601 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\00000"
-/* offset=2623 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\00000"
-/* offset=2643 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000"
+/* offset=0 */ "tool\000"
+/* offset=5 */ "duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000"
+/* offset=78 */ "user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000"
+/* offset=145 */ "system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000"
+/* offset=210 */ "has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000"
+/* offset=283 */ "num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000"
+/* offset=425 */ "num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000"
+/* offset=525 */ "num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000"
+/* offset=639 */ "num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000"
+/* offset=712 */ "num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000"
+/* offset=795 */ "slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000"
+/* offset=902 */ "smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000"
+/* offset=1006 */ "system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000"
+/* offset=1102 */ "default_core\000"
+/* offset=1115 */ "bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000"
+/* offset=1174 */ "bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000"
+/* offset=1233 */ "l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000"
+/* offset=1328 */ "segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000"
+/* offset=1427 */ "dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000"
+/* offset=1557 */ "eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000"
+/* offset=1672 */ "hisi_sccl,ddrc\000"
+/* offset=1687 */ "uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000"
+/* offset=1773 */ "uncore_cbox\000"
+/* offset=1785 */ "unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000"
+/* offset=2016 */ "event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000"
+/* offset=2081 */ "event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000"
+/* offset=2152 */ "hisi_sccl,l3c\000"
+/* offset=2166 */ "uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000"
+/* offset=2246 */ "uncore_imc_free_running\000"
+/* offset=2270 */ "uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000"
+/* offset=2365 */ "uncore_imc\000"
+/* offset=2376 */ "uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000"
+/* offset=2454 */ "uncore_sys_ddr_pmu\000"
+/* offset=2473 */ "sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000"
+/* offset=2546 */ "uncore_sys_ccn_pmu\000"
+/* offset=2565 */ "sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000"
+/* offset=2639 */ "uncore_sys_cmn_pmu\000"
+/* offset=2658 */ "sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000"
+/* offset=2798 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000"
+/* offset=2820 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000"
+/* offset=2883 */ "Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000"
+/* offset=3049 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000"
+/* offset=3113 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000"
+/* offset=3180 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000"
+/* offset=3251 */ "DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000"
+/* offset=3345 */ "DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000"
+/* offset=3479 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000"
+/* offset=3543 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000"
+/* offset=3611 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000"
+/* offset=3681 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\00000"
+/* offset=3703 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\00000"
+/* offset=3725 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\00000"
+/* offset=3745 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000"
;
+static const struct compact_pmu_event pmu_events__common_tool[] = {
+{ 5 }, /* duration_time\000tool\000Wall clock interval time in nanoseconds\000config=1\000\00000\000\000 */
+{ 210 }, /* has_pmem\000tool\0001 if persistent memory installed otherwise 0\000config=4\000\00000\000\000 */
+{ 283 }, /* num_cores\000tool\000Number of cores. A core consists of 1 or more thread, with each thread being associated with a logical Linux CPU\000config=5\000\00000\000\000 */
+{ 425 }, /* num_cpus\000tool\000Number of logical Linux CPUs. There may be multiple such CPUs on a core\000config=6\000\00000\000\000 */
+{ 525 }, /* num_cpus_online\000tool\000Number of online logical Linux CPUs. There may be multiple such CPUs on a core\000config=7\000\00000\000\000 */
+{ 639 }, /* num_dies\000tool\000Number of dies. Each die has 1 or more cores\000config=8\000\00000\000\000 */
+{ 712 }, /* num_packages\000tool\000Number of packages. Each package has 1 or more die\000config=9\000\00000\000\000 */
+{ 795 }, /* slots\000tool\000Number of functional units that in parallel can execute parts of an instruction\000config=0xa\000\00000\000\000 */
+{ 902 }, /* smt_on\000tool\0001 if simultaneous multithreading (aka hyperthreading) is enable otherwise 0\000config=0xb\000\00000\000\000 */
+{ 145 }, /* system_time\000tool\000System/kernel time in nanoseconds\000config=3\000\00000\000\000 */
+{ 1006 }, /* system_tsc_freq\000tool\000The amount a Time Stamp Counter (TSC) increases per second\000config=0xc\000\00000\000\000 */
+{ 78 }, /* user_time\000tool\000User (non-kernel) time in nanoseconds\000config=2\000\00000\000\000 */
+
+};
+
+const struct pmu_table_entry pmu_events__common[] = {
+{
+ .entries = pmu_events__common_tool,
+ .num_entries = ARRAY_SIZE(pmu_events__common_tool),
+ .pmu_name = { 0 /* tool\000 */ },
+},
+};
+
static const struct compact_pmu_event pmu_events__test_soc_cpu_default_core[] = {
-{ 13 }, /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000 */
-{ 72 }, /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000 */
-{ 325 }, /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000 */
-{ 455 }, /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000 */
-{ 131 }, /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000 */
-{ 226 }, /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000 */
+{ 1115 }, /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000 */
+{ 1174 }, /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000 */
+{ 1427 }, /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000 */
+{ 1557 }, /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000 */
+{ 1233 }, /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000 */
+{ 1328 }, /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000 */
};
static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_ddrc[] = {
-{ 585 }, /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000 */
+{ 1687 }, /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000 */
};
static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_l3c[] = {
-{ 1064 }, /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000 */
+{ 2166 }, /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000 */
};
static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_cbox[] = {
-{ 914 }, /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000 */
-{ 979 }, /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000 */
-{ 683 }, /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000 */
+{ 2016 }, /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000 */
+{ 2081 }, /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000 */
+{ 1785 }, /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000 */
};
static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc[] = {
-{ 1274 }, /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000 */
+{ 2376 }, /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000 */
};
static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc_free_running[] = {
-{ 1168 }, /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000 */
+{ 2270 }, /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000 */
};
@@ -92,51 +129,51 @@ const struct pmu_table_entry pmu_events__test_soc_cpu[] = {
{
.entries = pmu_events__test_soc_cpu_default_core,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_default_core),
- .pmu_name = { 0 /* default_core\000 */ },
+ .pmu_name = { 1102 /* default_core\000 */ },
},
{
.entries = pmu_events__test_soc_cpu_hisi_sccl_ddrc,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_ddrc),
- .pmu_name = { 570 /* hisi_sccl,ddrc\000 */ },
+ .pmu_name = { 1672 /* hisi_sccl,ddrc\000 */ },
},
{
.entries = pmu_events__test_soc_cpu_hisi_sccl_l3c,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_l3c),
- .pmu_name = { 1050 /* hisi_sccl,l3c\000 */ },
+ .pmu_name = { 2152 /* hisi_sccl,l3c\000 */ },
},
{
.entries = pmu_events__test_soc_cpu_uncore_cbox,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_cbox),
- .pmu_name = { 671 /* uncore_cbox\000 */ },
+ .pmu_name = { 1773 /* uncore_cbox\000 */ },
},
{
.entries = pmu_events__test_soc_cpu_uncore_imc,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc),
- .pmu_name = { 1263 /* uncore_imc\000 */ },
+ .pmu_name = { 2365 /* uncore_imc\000 */ },
},
{
.entries = pmu_events__test_soc_cpu_uncore_imc_free_running,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc_free_running),
- .pmu_name = { 1144 /* uncore_imc_free_running\000 */ },
+ .pmu_name = { 2246 /* uncore_imc_free_running\000 */ },
},
};
static const struct compact_pmu_event pmu_metrics__test_soc_cpu_default_core[] = {
-{ 1696 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000 */
-{ 2377 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000 */
-{ 2149 }, /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000 */
-{ 2243 }, /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000 */
-{ 2441 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000 */
-{ 2509 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000 */
-{ 1781 }, /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000 */
-{ 1718 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000 */
-{ 2643 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000 */
-{ 2579 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\00000 */
-{ 2601 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\00000 */
-{ 2623 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\00000 */
-{ 2078 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000 */
-{ 1947 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */
-{ 2011 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */
+{ 2798 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000 */
+{ 3479 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000 */
+{ 3251 }, /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000 */
+{ 3345 }, /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000 */
+{ 3543 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000 */
+{ 3611 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000 */
+{ 2883 }, /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000 */
+{ 2820 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000 */
+{ 3745 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000 */
+{ 3681 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\00000 */
+{ 3703 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\00000 */
+{ 3725 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\00000 */
+{ 3180 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000 */
+{ 3049 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */
+{ 3113 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */
};
@@ -144,18 +181,18 @@ const struct pmu_table_entry pmu_metrics__test_soc_cpu[] = {
{
.entries = pmu_metrics__test_soc_cpu_default_core,
.num_entries = ARRAY_SIZE(pmu_metrics__test_soc_cpu_default_core),
- .pmu_name = { 0 /* default_core\000 */ },
+ .pmu_name = { 1102 /* default_core\000 */ },
},
};
static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ccn_pmu[] = {
-{ 1463 }, /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000 */
+{ 2565 }, /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000 */
};
static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_cmn_pmu[] = {
-{ 1556 }, /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000 */
+{ 2658 }, /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000 */
};
static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ddr_pmu[] = {
-{ 1371 }, /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000 */
+{ 2473 }, /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000 */
};
@@ -163,17 +200,17 @@ const struct pmu_table_entry pmu_events__test_soc_sys[] = {
{
.entries = pmu_events__test_soc_sys_uncore_sys_ccn_pmu,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ccn_pmu),
- .pmu_name = { 1444 /* uncore_sys_ccn_pmu\000 */ },
+ .pmu_name = { 2546 /* uncore_sys_ccn_pmu\000 */ },
},
{
.entries = pmu_events__test_soc_sys_uncore_sys_cmn_pmu,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_cmn_pmu),
- .pmu_name = { 1537 /* uncore_sys_cmn_pmu\000 */ },
+ .pmu_name = { 2639 /* uncore_sys_cmn_pmu\000 */ },
},
{
.entries = pmu_events__test_soc_sys_uncore_sys_ddr_pmu,
.num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ddr_pmu),
- .pmu_name = { 1352 /* uncore_sys_ddr_pmu\000 */ },
+ .pmu_name = { 2454 /* uncore_sys_ddr_pmu\000 */ },
},
};
@@ -210,6 +247,15 @@ struct pmu_events_map {
* table of PMU events.
*/
const struct pmu_events_map pmu_events_map[] = {
+{
+ .arch = "common",
+ .cpuid = "common",
+ .event_table = {
+ .pmus = pmu_events__common,
+ .num_pmus = ARRAY_SIZE(pmu_events__common),
+ },
+ .metric_table = {},
+},
{
.arch = "testarch",
.cpuid = "testcpu",
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index bb0a5d92df4a..3d2c4e6efeb4 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -292,6 +292,7 @@ class JsonEvent:
'cpu_atom': 'cpu_atom',
'ali_drw': 'ali_drw',
'arm_cmn': 'arm_cmn',
+ 'tool': 'tool',
}
return table[unit] if unit in table else f'uncore_{unit.lower()}'
@@ -721,6 +722,17 @@ const struct pmu_events_map pmu_events_map[] = {
\t\t.num_pmus = ARRAY_SIZE(pmu_metrics__test_soc_cpu),
\t}
},
+""")
+ elif arch == 'common':
+ _args.output_file.write("""{
+\t.arch = "common",
+\t.cpuid = "common",
+\t.event_table = {
+\t\t.pmus = pmu_events__common,
+\t\t.num_pmus = ARRAY_SIZE(pmu_events__common),
+\t},
+\t.metric_table = {},
+},
""")
else:
with open(f'{_args.starting_dir}/{arch}/mapfile.csv') as csvfile:
@@ -1241,7 +1253,7 @@ def main() -> None:
if len(parents) == _args.model.split(',')[0].count('/'):
# We're testing the correct directory.
item_path = '/'.join(parents) + ('/' if len(parents) > 0 else '') + item.name
- if 'test' not in item_path and item_path not in _args.model.split(','):
+ if 'test' not in item_path and 'common' not in item_path and item_path not in _args.model.split(','):
continue
action(parents, item)
if item.is_dir():
@@ -1289,7 +1301,7 @@ struct pmu_table_entry {
for item in os.scandir(_args.starting_dir):
if not item.is_dir():
continue
- if item.name == _args.arch or _args.arch == 'all' or item.name == 'test':
+ if item.name == _args.arch or _args.arch == 'all' or item.name == 'test' or item.name == 'common':
archs.append(item.name)
if len(archs) < 2 and _args.arch != 'none':
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 09/13] perf tool_pmu: Switch to standard pmu functions and json descriptions
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (7 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 08/13] perf jevents: Add tool event json under a common architecture Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 10/13] perf tests: Add tool PMU test Ian Rogers
` (4 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Use the regular PMU approaches with tool json events to reduce the
amount of special tool_pmu code - tool_pmu__config_terms and
tool_pmu__for_each_event_cb are removed. Some functions remain, like
tool_pmu__str_to_event, as conveniences to metricgroups. Add
tool_pmu__skip_event/tool_pmu__num_skip_events to handle the case that
tool json events shouldn't appear on certain architectures. This isn't
done in jevents.py due to complexity in the empty-pmu-events.c and
when all vendor json is built into the tool.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/pmu.c | 26 ++++-----
tools/perf/util/pmus.c | 4 +-
tools/perf/util/tool_pmu.c | 109 +++++++++++--------------------------
tools/perf/util/tool_pmu.h | 12 +---
4 files changed, 46 insertions(+), 105 deletions(-)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 9cb7a4e5e7d4..beb93e2603e2 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1549,9 +1549,6 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
{
bool zero = !!pmu->perf_event_attr_init_default;
- if (perf_pmu__is_tool(pmu))
- return tool_pmu__config_terms(attr, head_terms, err);
-
/* Fake PMU doesn't have proper terms so nothing to configure in attr. */
if (perf_pmu__is_fake(pmu))
return 0;
@@ -1664,8 +1661,8 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_
info->scale = 0.0;
info->snapshot = false;
- /* Tool/fake PMU doesn't rewrite terms. */
- if (perf_pmu__is_tool(pmu) || perf_pmu__is_fake(pmu))
+ /* Fake PMU doesn't rewrite terms. */
+ if (perf_pmu__is_fake(pmu))
goto out;
list_for_each_entry_safe(term, h, &head_terms->terms, list) {
@@ -1831,8 +1828,8 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
{
if (!name)
return false;
- if (perf_pmu__is_tool(pmu))
- return tool_pmu__str_to_event(name) != TOOL_PMU__EVENT_NONE;
+ if (perf_pmu__is_tool(pmu) && tool_pmu__skip_event(name))
+ return false;
if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
return true;
if (pmu->cpu_aliases_added || !pmu->events_table)
@@ -1844,9 +1841,6 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu)
{
size_t nr;
- if (perf_pmu__is_tool(pmu))
- return tool_pmu__num_events();
-
pmu_aliases_parse(pmu);
nr = pmu->sysfs_aliases + pmu->sys_json_aliases;
@@ -1857,6 +1851,9 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu)
else
assert(pmu->cpu_json_aliases == 0);
+ if (perf_pmu__is_tool(pmu))
+ nr -= tool_pmu__num_skip_events();
+
return pmu->selectable ? nr + 1 : nr;
}
@@ -1907,15 +1904,15 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
int ret = 0;
struct strbuf sb;
- if (perf_pmu__is_tool(pmu))
- return tool_pmu__for_each_event_cb(pmu, state, cb);
-
strbuf_init(&sb, /*hint=*/ 0);
pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu);
list_for_each_entry(event, &pmu->aliases, list) {
size_t buf_used, pmu_name_len;
+ if (perf_pmu__is_tool(pmu) && tool_pmu__skip_event(event->name))
+ continue;
+
info.pmu_name = event->pmu_name ?: pmu->name;
pmu_name_len = pmu_deduped_name_len(pmu, info.pmu_name,
skip_duplicate_pmus);
@@ -2321,9 +2318,6 @@ const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config)
if (!pmu)
return NULL;
- if (perf_pmu__is_tool(pmu))
- return tool_pmu__event_to_str(config);
-
pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu);
list_for_each_entry(event, &pmu->aliases, list) {
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 6e657c44de58..107de86c2637 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -440,6 +440,7 @@ static int perf_pmus__print_pmu_events__callback(void *vstate,
pr_err("Unexpected event %s/%s/\n", info->pmu->name, info->name);
return 1;
}
+ assert(info->pmu != NULL || info->name != NULL);
s = &state->aliases[state->index];
s->pmu = info->pmu;
#define COPY_STR(str) s->str = info->str ? strdup(info->str) : NULL
@@ -590,9 +591,6 @@ void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, voi
int len = pmu_name_len_no_suffix(pmu->name);
const char *desc = "(see 'man perf-list' or 'man perf-record' on how to encode it)";
- if (perf_pmu__is_tool(pmu))
- continue;
-
if (!pmu->is_core)
desc = NULL;
diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c
index 3768c6a42ff6..488c9db33e27 100644
--- a/tools/perf/util/tool_pmu.c
+++ b/tools/perf/util/tool_pmu.c
@@ -33,101 +33,54 @@ static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] = {
"system_tsc_freq",
};
-
-const char *tool_pmu__event_to_str(enum tool_pmu_event ev)
-{
- if (ev > TOOL_PMU__EVENT_NONE && ev < TOOL_PMU__EVENT_MAX)
- return tool_pmu__event_names[ev];
-
- return NULL;
-}
-
-enum tool_pmu_event tool_pmu__str_to_event(const char *str)
+bool tool_pmu__skip_event(const char *name __maybe_unused)
{
- int i;
-
- tool_pmu__for_each_event(i) {
- if (!strcasecmp(str, tool_pmu__event_names[i])) {
#if !defined(__aarch64__)
- /* The slots event should only appear on arm64. */
- if (i == TOOL_PMU__EVENT_SLOTS)
- return TOOL_PMU__EVENT_NONE;
+ /* The slots event should only appear on arm64. */
+ if (strcasecmp(name, "slots") == 0)
+ return true;
#endif
- return i;
- }
- }
- return TOOL_PMU__EVENT_NONE;
+#if !defined(__i386__) && !defined(__x86_64__)
+ /* The system_tsc_freq event should only appear on x86. */
+ if (strcasecmp(name, "system_tsc_freq") == 0)
+ return true;
+#endif
+ return false;
}
-static int tool_pmu__config_term(struct perf_event_attr *attr,
- struct parse_events_term *term,
- struct parse_events_error *err)
+int tool_pmu__num_skip_events(void)
{
- if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
- enum tool_pmu_event ev = tool_pmu__str_to_event(term->config);
+ int num = 0;
- if (ev == TOOL_PMU__EVENT_NONE)
- goto err_out;
-
- attr->config = ev;
- return 0;
- }
-err_out:
- if (err) {
- char *err_str;
-
- parse_events_error__handle(err, term->err_val,
- asprintf(&err_str,
- "unexpected tool event term (%s) %s",
- parse_events__term_type_str(term->type_term),
- term->config) < 0
- ? strdup("unexpected tool event term")
- : err_str,
- NULL);
- }
- return -EINVAL;
+#if !defined(__aarch64__)
+ num++;
+#endif
+#if !defined(__i386__) && !defined(__x86_64__)
+ num++;
+#endif
+ return num;
}
-int tool_pmu__config_terms(struct perf_event_attr *attr,
- struct parse_events_terms *terms,
- struct parse_events_error *err)
+const char *tool_pmu__event_to_str(enum tool_pmu_event ev)
{
- struct parse_events_term *term;
-
- list_for_each_entry(term, &terms->terms, list) {
- if (tool_pmu__config_term(attr, term, err))
- return -EINVAL;
- }
-
- return 0;
+ if (ev > TOOL_PMU__EVENT_NONE && ev < TOOL_PMU__EVENT_MAX)
+ return tool_pmu__event_names[ev];
+ return NULL;
}
-int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
+enum tool_pmu_event tool_pmu__str_to_event(const char *str)
{
- struct pmu_event_info info = {
- .pmu = pmu,
- .event_type_desc = "Tool event",
- };
int i;
+ if (tool_pmu__skip_event(str))
+ return TOOL_PMU__EVENT_NONE;
+
tool_pmu__for_each_event(i) {
- int ret;
-
- info.name = tool_pmu__event_to_str(i);
- info.alias = NULL;
- info.scale_unit = NULL;
- info.desc = NULL;
- info.long_desc = NULL;
- info.encoding_desc = NULL;
- info.topic = NULL;
- info.pmu_name = pmu->name;
- info.deprecated = false;
- ret = cb(state, &info);
- if (ret)
- return ret;
+ if (!strcasecmp(str, tool_pmu__event_names[i]))
+ return i;
}
- return 0;
+ return TOOL_PMU__EVENT_NONE;
}
bool perf_pmu__is_tool(const struct perf_pmu *pmu)
@@ -548,6 +501,8 @@ struct perf_pmu *perf_pmus__tool_pmu(void)
.caps = LIST_HEAD_INIT(tool.caps),
.format = LIST_HEAD_INIT(tool.format),
};
+ if (!tool.events_table)
+ tool.events_table = find_core_events_table("common", "common");
return &tool;
}
diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h
index ecdf316525bb..a60184859080 100644
--- a/tools/perf/util/tool_pmu.h
+++ b/tools/perf/util/tool_pmu.h
@@ -29,17 +29,11 @@ enum tool_pmu_event {
#define tool_pmu__for_each_event(ev) \
for ((ev) = TOOL_PMU__EVENT_DURATION_TIME; (ev) < TOOL_PMU__EVENT_MAX; ev++)
-static inline size_t tool_pmu__num_events(void)
-{
- return TOOL_PMU__EVENT_MAX - 1;
-}
-
const char *tool_pmu__event_to_str(enum tool_pmu_event ev);
enum tool_pmu_event tool_pmu__str_to_event(const char *str);
-int tool_pmu__config_terms(struct perf_event_attr *attr,
- struct parse_events_terms *terms,
- struct parse_events_error *err);
-int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb);
+bool tool_pmu__skip_event(const char *name);
+int tool_pmu__num_skip_events(void);
+
bool tool_pmu__read_event(enum tool_pmu_event ev, u64 *result);
u64 tool_pmu__cpu_slots_per_cycle(void);
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 10/13] perf tests: Add tool PMU test
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (8 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 09/13] perf tool_pmu: Switch to standard pmu functions and json descriptions Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 11/13] perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs Ian Rogers
` (3 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Ensure parsing with and without PMU creates events with the expected
config values. This ensures the tool.json doesn't get out of sync with
tool_pmu_event enum.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/tests.h | 1 +
tools/perf/tests/tool_pmu.c | 111 ++++++++++++++++++++++++++++++++
4 files changed, 114 insertions(+)
create mode 100644 tools/perf/tests/tool_pmu.c
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 5671ee530019..a771e4928247 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -67,6 +67,7 @@ perf-test-y += sigtrap.o
perf-test-y += event_groups.o
perf-test-y += symbols.o
perf-test-y += util.o
+perf-test-y += tool_pmu.o
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc))
perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 470a9709427d..3b30f258c395 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -73,6 +73,7 @@ static struct test_suite *generic_tests[] = {
&suite__PERF_RECORD,
&suite__pmu,
&suite__pmu_events,
+ &suite__tool_pmu,
&suite__dso_data,
&suite__perf_evsel__roundtrip_name_test,
#ifdef HAVE_LIBTRACEEVENT
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 6ea2be86b7bf..1ed76d4156b6 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -83,6 +83,7 @@ DECLARE_SUITE(perf_evsel__tp_sched_test);
DECLARE_SUITE(syscall_openat_tp_fields);
DECLARE_SUITE(pmu);
DECLARE_SUITE(pmu_events);
+DECLARE_SUITE(tool_pmu);
DECLARE_SUITE(attr);
DECLARE_SUITE(dso_data);
DECLARE_SUITE(dso_data_cache);
diff --git a/tools/perf/tests/tool_pmu.c b/tools/perf/tests/tool_pmu.c
new file mode 100644
index 000000000000..94d0dd8fd3cb
--- /dev/null
+++ b/tools/perf/tests/tool_pmu.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+#include "debug.h"
+#include "evlist.h"
+#include "parse-events.h"
+#include "tests.h"
+#include "tool_pmu.h"
+
+static int do_test(enum tool_pmu_event ev, bool with_pmu)
+{
+ struct evlist *evlist = evlist__new();
+ struct evsel *evsel;
+ struct parse_events_error err;
+ int ret;
+ char str[128];
+ bool found = false;
+
+ if (!evlist) {
+ pr_err("evlist allocation failed\n");
+ return TEST_FAIL;
+ }
+
+ if (with_pmu)
+ snprintf(str, sizeof(str), "tool/%s/", tool_pmu__event_to_str(ev));
+ else
+ strncpy(str, tool_pmu__event_to_str(ev), sizeof(str));
+
+ parse_events_error__init(&err);
+ ret = parse_events(evlist, str, &err);
+ if (ret) {
+ evlist__delete(evlist);
+ if (tool_pmu__skip_event(tool_pmu__event_to_str(ev))) {
+ ret = TEST_OK;
+ goto out;
+ }
+
+ pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n",
+ __FILE__, __LINE__, str, ret);
+ parse_events_error__print(&err, str);
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ ret = TEST_OK;
+ if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) {
+ pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n",
+ __FILE__, __LINE__, str, evlist->core.nr_entries);
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ evlist__for_each_entry(evlist, evsel) {
+ if (perf_pmu__is_tool(evsel->pmu)) {
+ if (evsel->core.attr.config != ev) {
+ pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %d\n",
+ __FILE__, __LINE__, str, evsel->core.attr.config, ev);
+ ret = TEST_FAIL;
+ goto out;
+ }
+ found = true;
+ }
+ }
+
+ if (!found && !tool_pmu__skip_event(tool_pmu__event_to_str(ev))) {
+ pr_debug("FAILED %s:%d Didn't find tool event '%s' in parsed evsels\n",
+ __FILE__, __LINE__, str);
+ ret = TEST_FAIL;
+ }
+
+out:
+ evlist__delete(evlist);
+ return ret;
+}
+
+static int test__tool_pmu_without_pmu(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ int i;
+
+ tool_pmu__for_each_event(i) {
+ int ret = do_test(i, /*with_pmu=*/false);
+
+ if (ret != TEST_OK)
+ return ret;
+ }
+ return TEST_OK;
+}
+
+static int test__tool_pmu_with_pmu(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ int i;
+
+ tool_pmu__for_each_event(i) {
+ int ret = do_test(i, /*with_pmu=*/true);
+
+ if (ret != TEST_OK)
+ return ret;
+ }
+ return TEST_OK;
+}
+
+static struct test_case tests__tool_pmu[] = {
+ TEST_CASE("Parsing without PMU name", tool_pmu_without_pmu),
+ TEST_CASE("Parsing with PMU name", tool_pmu_with_pmu),
+ { .name = NULL, }
+};
+
+struct test_suite suite__tool_pmu = {
+ .desc = "Tool PMU",
+ .test_cases = tests__tool_pmu,
+};
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 11/13] perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (9 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 10/13] perf tests: Add tool PMU test Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 12/13] perf test: Add hwmon "PMU" test Ian Rogers
` (2 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
The hwmon sysfs ABI is defined in
Documentation/hwmon/sysfs-interface.rst. Create a PMU that reads the
hwmon input and can be used in `perf stat` and metrics much as an
uncore PMU can.
For example, the following shows reading the CPU temperature and 2 fan
speeds alongside the uncore frequency:
```
$ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
1.001153138 52.00 'C temp_cpu
1.001153138 2,588 rpm fan1
1.001153138 2,482 rpm hwmon_thinkpad/fan2/
1.001153138 8 tool/num_cpus_online/
1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
1.001153138 1,012,773,595 duration_time
...
```
The PMUs are named from /sys/class/hwmon/hwmon<num>/name and have an
alias of hwmon<num>. The events are naned using the _label files as
well as the <type><num> prefix, the latter guaranteed to be unique.
In `perf list` the other hwmon files are used to give a richer
description, for example:
```
hwmon:
temp1
[Temperature in unit acpitz named temp1. Unit: hwmon_acpitz]
in0
[Voltage in unit bat0 named in0. Unit: hwmon_bat0]
temp_core_0 OR temp2
[Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
hwmon_coretemp]
temp_core_1 OR temp3
[Temperature in unit coretemp named Core 1. crit=100'C,max=100'C crit_alarm=0'C. Unit:
hwmon_coretemp]
...
temp_package_id_0 OR temp1
[Temperature in unit coretemp named Package id 0. crit=100'C,max=100'C crit_alarm=0'C.
Unit: hwmon_coretemp]
temp1
[Temperature in unit iwlwifi_1 named temp1. Unit: hwmon_iwlwifi_1]
temp_composite OR temp1
[Temperature in unit nvme named Composite. alarm=0'C,crit=86.85'C,max=75.85'C,
min=-273.15'C. Unit: hwmon_nvme]
temp_sensor_1 OR temp2
[Temperature in unit nvme named Sensor 1. max=65261.8'C,min=-273.15'C. Unit: hwmon_nvme]
temp_sensor_2 OR temp3
[Temperature in unit nvme named Sensor 2. max=65261.8'C,min=-273.15'C. Unit: hwmon_nvme]
fan1
[Fan in unit thinkpad named fan1. Unit: hwmon_thinkpad]
fan2
[Fan in unit thinkpad named fan2. Unit: hwmon_thinkpad]
...
temp_cpu OR temp1
[Temperature in unit thinkpad named CPU. Unit: hwmon_thinkpad]
temp_gpu OR temp2
[Temperature in unit thinkpad named GPU. Unit: hwmon_thinkpad]
curr1
[Current in unit ucsi_source_psy_usbc000_0 named curr1. max=1.5A. Unit:
hwmon_ucsi_source_psy_usbc000_0]
in0
[Voltage in unit ucsi_source_psy_usbc000_0 named in0. max=5V,min=5V. Unit:
hwmon_ucsi_source_psy_usbc000_0]
```
As there may be multiple hwmon devices a range of PMU types are
reserved for their use and to identify the PMU as belonging to the
hwmon types.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/Build | 1 +
tools/perf/util/evsel.c | 9 +
tools/perf/util/hwmon_pmu.c | 818 ++++++++++++++++++++++++++++++++++++
tools/perf/util/hwmon_pmu.h | 154 +++++++
tools/perf/util/pmu.c | 20 +
tools/perf/util/pmu.h | 2 +
tools/perf/util/pmus.c | 2 +
7 files changed, 1006 insertions(+)
create mode 100644 tools/perf/util/hwmon_pmu.c
create mode 100644 tools/perf/util/hwmon_pmu.h
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index fa508e113dd0..9eb8b1c3df94 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -83,6 +83,7 @@ perf-util-y += pmu.o
perf-util-y += pmus.o
perf-util-y += pmu-flex.o
perf-util-y += pmu-bison.o
+perf-util-y += hwmon_pmu.o
perf-util-y += tool_pmu.o
perf-util-y += svghelper.o
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-info.o
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 9e748ed20988..64883d2aa1bb 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -50,6 +50,7 @@
#include "off_cpu.h"
#include "pmu.h"
#include "pmus.h"
+#include "hwmon_pmu.h"
#include "tool_pmu.h"
#include "rlimit.h"
#include "../perf-sys.h"
@@ -1657,6 +1658,9 @@ int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread)
if (evsel__is_tool(evsel))
return evsel__tool_pmu_read(evsel, cpu_map_idx, thread);
+ if (evsel__is_hwmon(evsel))
+ return evsel__hwmon_pmu_read(evsel, cpu_map_idx, thread);
+
if (evsel__is_retire_lat(evsel))
return evsel__read_retire_lat(evsel, cpu_map_idx, thread);
@@ -2094,6 +2098,11 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
start_cpu_map_idx,
end_cpu_map_idx);
}
+ if (evsel__is_hwmon(evsel)) {
+ return evsel__hwmon_pmu_open(evsel, threads,
+ start_cpu_map_idx,
+ end_cpu_map_idx);
+ }
for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c
new file mode 100644
index 000000000000..142584018a4f
--- /dev/null
+++ b/tools/perf/util/hwmon_pmu.c
@@ -0,0 +1,818 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+#include "counts.h"
+#include "debug.h"
+#include "evsel.h"
+#include "hashmap.h"
+#include "hwmon_pmu.h"
+#include "pmu.h"
+#include <internal/xyarray.h>
+#include <internal/threadmap.h>
+#include <perf/threadmap.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <api/fs/fs.h>
+#include <api/io.h>
+#include <linux/zalloc.h>
+
+const char * const hwmon_type_strs[HWMON_TYPE_MAX] = {
+ NULL,
+ "cpu",
+ "curr",
+ "energy",
+ "fan",
+ "humidity",
+ "in",
+ "intrusion",
+ "power",
+ "pwm",
+ "temp",
+};
+
+static const char *const hwmon_units[HWMON_TYPE_MAX] = {
+ NULL,
+ "V", /* cpu */
+ "A", /* curr */
+ "J", /* energy */
+ "rpm", /* fan */
+ "%", /* humidity */
+ "V", /* in */
+ "", /* intrusion */
+ "W", /* power */
+ "Hz", /* pwm */
+ "'C", /* temp */
+};
+
+const char * const hwmon_item_strs[HWMON_ITEM__MAX] = {
+ NULL,
+ "accuracy",
+ "alarm",
+ "auto_channels_temp",
+ "average",
+ "average_highest",
+ "average_interval",
+ "average_interval_max",
+ "average_interval_min",
+ "average_lowest",
+ "average_max",
+ "average_min",
+ "beep",
+ "cap",
+ "cap_hyst",
+ "cap_max",
+ "cap_min",
+ "crit",
+ "crit_hyst",
+ "div",
+ "emergency",
+ "emergency_hist",
+ "enable",
+ "fault",
+ "freq",
+ "highest",
+ "input",
+ "label",
+ "lcrit",
+ "lcrit_hyst",
+ "lowest",
+ "max",
+ "max_hyst",
+ "min",
+ "min_hyst",
+ "mod",
+ "offset",
+ "pulses",
+ "rated_max",
+ "rated_min",
+ "reset_history",
+ "target",
+ "type",
+ "vid",
+};
+
+struct hwmon_pmu {
+ struct perf_pmu pmu;
+ struct hashmap events;
+ int hwmon_dir_fd;
+};
+
+/**
+ * union hwmon_pmu_event_key: Key for hwmon_pmu->events as such each key
+ * represents an event.
+ *
+ * Related hwmon files start <type><number> that this key represents.
+ */
+union hwmon_pmu_event_key {
+ long type_and_num;
+ struct {
+ int num :16;
+ enum hwmon_type type :8;
+ };
+};
+
+/**
+ * struct hwmon_pmu_event_value: Value in hwmon_pmu->events.
+ *
+ * Hwmon files are of the form <type><number>_<item> and may have a suffix
+ * _alarm.
+ */
+struct hwmon_pmu_event_value {
+ /** @items: which item files are present. */
+ DECLARE_BITMAP(items, HWMON_ITEM__MAX);
+ /** @alarm_items: which item files are present. */
+ DECLARE_BITMAP(alarm_items, HWMON_ITEM__MAX);
+ /** @label: contents of <type><number>_label if present. */
+ char *label;
+ /** @name: name computed from label of the form <type>_<label>. */
+ char *name;
+};
+
+bool perf_pmu__is_hwmon(const struct perf_pmu *pmu)
+{
+ return pmu && pmu->type >= PERF_PMU_TYPE_HWMON_START &&
+ pmu->type <= PERF_PMU_TYPE_HWMON_END;
+}
+
+bool evsel__is_hwmon(const struct evsel *evsel)
+{
+ return perf_pmu__is_hwmon(evsel->pmu);
+}
+
+static size_t hwmon_pmu__event_hashmap_hash(long key, void *ctx __maybe_unused)
+{
+ return ((union hwmon_pmu_event_key)key).type_and_num;
+}
+
+static bool hwmon_pmu__event_hashmap_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return ((union hwmon_pmu_event_key)key1).type_and_num ==
+ ((union hwmon_pmu_event_key)key2).type_and_num;
+}
+
+static int hwmon_strcmp(const void *a, const void *b)
+{
+ const char *sa = a;
+ const char * const *sb = b;
+
+ return strcmp(sa, *sb);
+}
+
+bool parse_hwmon_filename(const char *filename,
+ enum hwmon_type *type,
+ int *number,
+ enum hwmon_item *item,
+ bool *alarm)
+{
+ char fn_type[24];
+ const char **elem;
+ const char *fn_item = NULL;
+ size_t fn_item_len;
+
+ assert(strlen("intrusion") < sizeof(fn_type));
+ strncpy(fn_type, filename, sizeof(fn_type));
+ for (size_t i = 0; i < sizeof(fn_type); i++) {
+ if (fn_type[i] >= '0' && fn_type[i] <= '9') {
+ fn_type[i] = '\0';
+ *number = strtoul(&filename[i], (char **)&fn_item, 10);
+ if (*fn_item == '_')
+ fn_item++;
+ break;
+ }
+ if (fn_type[i] == '_') {
+ fn_type[i] = '\0';
+ *number = -1;
+ fn_item = &filename[i + 1];
+ break;
+ }
+ }
+ if (fn_item == NULL || fn_type[0] == '\0' || (item != NULL && fn_item[0] == '\0')) {
+ pr_debug("hwmon_pmu: not a hwmon file '%s'\n", filename);
+ return false;
+ }
+ elem = bsearch(&fn_type, hwmon_type_strs + 1, ARRAY_SIZE(hwmon_type_strs) - 1,
+ sizeof(hwmon_type_strs[0]), hwmon_strcmp);
+ if (!elem) {
+ pr_debug("hwmon_pmu: not a hwmon type '%s' in file name '%s'\n",
+ fn_type, filename);
+ return false;
+ }
+
+ *type = elem - &hwmon_type_strs[0];
+ if (!item)
+ return true;
+
+ *alarm = false;
+ fn_item_len = strlen(fn_item);
+ if (fn_item_len > 6 && !strcmp(&fn_item[fn_item_len - 6], "_alarm")) {
+ assert(strlen("average_interval_max") < sizeof(fn_type));
+ strncpy(fn_type, fn_item, fn_item_len - 6);
+ fn_item = fn_type;
+ *alarm = true;
+ }
+ elem = bsearch(fn_item, hwmon_item_strs + 1, ARRAY_SIZE(hwmon_item_strs) - 1,
+ sizeof(hwmon_item_strs[0]), hwmon_strcmp);
+ if (!elem) {
+ pr_debug("hwmon_pmu: not a hwmon item '%s' in file name '%s'\n",
+ fn_item, filename);
+ return false;
+ }
+ *item = elem - &hwmon_item_strs[0];
+ return true;
+}
+
+static void fix_name(char *p)
+{
+ char *s = strchr(p, '\n');
+
+ if (s)
+ *s = '\0';
+
+ while (*p != '\0') {
+ if (strchr(" :,/\n\t", *p))
+ *p = '_';
+ else
+ *p = tolower(*p);
+ p++;
+ }
+}
+
+static int hwmon_pmu__read_events(struct hwmon_pmu *pmu)
+{
+ DIR *dir;
+ struct dirent *ent;
+ int dup_fd, err = 0;
+ struct hashmap_entry *cur, *tmp;
+ size_t bkt;
+
+ if (pmu->pmu.sysfs_aliases_loaded)
+ return 0;
+
+ /* Use a dup-ed fd as closedir will close it. */
+ dup_fd = dup(pmu->hwmon_dir_fd);
+ if (dup_fd == -1)
+ return -ENOMEM;
+
+ dir = fdopendir(dup_fd);
+ if (!dir) {
+ close(dup_fd);
+ return -ENOMEM;
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+ enum hwmon_type type;
+ int number;
+ enum hwmon_item item;
+ bool alarm;
+ union hwmon_pmu_event_key key = {};
+ struct hwmon_pmu_event_value *value;
+
+ if (ent->d_type != DT_REG)
+ continue;
+
+ if (!parse_hwmon_filename(ent->d_name, &type, &number, &item, &alarm)) {
+ pr_debug("Not a hwmon file '%s'\n", ent->d_name);
+ continue;
+ }
+ key.num = number;
+ key.type = type;
+ if (!hashmap__find(&pmu->events, key.type_and_num, &value)) {
+ value = zalloc(sizeof(*value));
+ if (!value) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ err = hashmap__add(&pmu->events, key.type_and_num, value);
+ if (err) {
+ free(value);
+ err = -ENOMEM;
+ goto err_out;
+ }
+ }
+ __set_bit(item, alarm ? value->alarm_items : value->items);
+ if (item == HWMON_ITEM_LABEL) {
+ char buf[128];
+ int fd = openat(pmu->hwmon_dir_fd, ent->d_name, O_RDONLY);
+ ssize_t read_len;
+
+ if (fd < 0)
+ continue;
+
+ read_len = read(fd, buf, sizeof(buf));
+
+ while (read_len > 0 && buf[read_len - 1] == '\n')
+ read_len--;
+
+ if (read_len > 0)
+ buf[read_len] = '\0';
+
+ if (buf[0] == '\0') {
+ pr_debug("hwmon_pmu: empty label file %s %s\n",
+ pmu->pmu.name, ent->d_name);
+ close(fd);
+ continue;
+ }
+ value->label = strdup(buf);
+ if (!value->label) {
+ pr_debug("hwmon_pmu: memory allocation failure\n");
+ close(fd);
+ continue;
+ }
+ snprintf(buf, sizeof(buf), "%s_%s", hwmon_type_strs[type], value->label);
+ fix_name(buf);
+ value->name = strdup(buf);
+ if (!value->name)
+ pr_debug("hwmon_pmu: memory allocation failure\n");
+ close(fd);
+ }
+ }
+ hashmap__for_each_entry_safe((&pmu->events), cur, tmp, bkt) {
+ union hwmon_pmu_event_key key = {
+ .type_and_num = cur->key,
+ };
+ struct hwmon_pmu_event_value *value = cur->pvalue;
+
+ if (!test_bit(HWMON_ITEM_INPUT, value->items)) {
+ pr_debug("hwmon_pmu: removing event '%s%d' that has no input file\n",
+ hwmon_type_strs[key.type], key.num);
+ hashmap__delete(&pmu->events, key.type_and_num, &key, &value);
+ zfree(&value->label);
+ zfree(&value->name);
+ free(value);
+ }
+ }
+ pmu->pmu.sysfs_aliases_loaded = true;
+
+err_out:
+ closedir(dir);
+ return err;
+}
+
+struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, int hwmon_dir, const char *sysfs_name, const char *name)
+{
+ char buf[32];
+ struct hwmon_pmu *hwm;
+
+ hwm = zalloc(sizeof(*hwm));
+ if (!hwm)
+ return NULL;
+
+
+ hwm->hwmon_dir_fd = hwmon_dir;
+ hwm->pmu.type = PERF_PMU_TYPE_HWMON_START + strtoul(sysfs_name + 5, NULL, 10);
+ if (hwm->pmu.type > PERF_PMU_TYPE_HWMON_END) {
+ pr_err("Unable to encode hwmon type from %s in valid PMU type\n", sysfs_name);
+ goto err_out;
+ }
+ snprintf(buf, sizeof(buf), "hwmon_%s", name);
+ fix_name(buf + 6);
+ hwm->pmu.name = strdup(buf);
+ if (!hwm->pmu.name)
+ goto err_out;
+ hwm->pmu.alias_name = strdup(sysfs_name);
+ if (!hwm->pmu.alias_name)
+ goto err_out;
+ hwm->pmu.cpus = perf_cpu_map__new("0");
+ if (!hwm->pmu.cpus)
+ goto err_out;
+ INIT_LIST_HEAD(&hwm->pmu.format);
+ INIT_LIST_HEAD(&hwm->pmu.aliases);
+ INIT_LIST_HEAD(&hwm->pmu.caps);
+ hashmap__init(&hwm->events, hwmon_pmu__event_hashmap_hash,
+ hwmon_pmu__event_hashmap_equal, /*ctx=*/NULL);
+
+ list_add_tail(&hwm->pmu.list, pmus);
+ return &hwm->pmu;
+err_out:
+ free((char *)hwm->pmu.name);
+ free(hwm->pmu.alias_name);
+ free(hwm);
+ close(hwmon_dir);
+ return NULL;
+}
+
+void hwmon_pmu__exit(struct perf_pmu *pmu)
+{
+ struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
+ struct hashmap_entry *cur, *tmp;
+ size_t bkt;
+
+ hashmap__for_each_entry_safe((&hwm->events), cur, tmp, bkt) {
+ struct hwmon_pmu_event_value *value = cur->pvalue;
+
+ zfree(&value->label);
+ zfree(&value->name);
+ free(value);
+ }
+ hashmap__clear(&hwm->events);
+ close (hwm->hwmon_dir_fd);
+}
+
+static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, size_t out_buf_len,
+ union hwmon_pmu_event_key key,
+ const unsigned long *items, bool is_alarm)
+{
+ size_t bit;
+ char buf[64];
+ size_t len = 0;
+
+ for_each_set_bit(bit, items, HWMON_ITEM__MAX) {
+ int fd;
+
+ if (bit == HWMON_ITEM_LABEL || bit == HWMON_ITEM_INPUT)
+ continue;
+
+ snprintf(buf, sizeof(buf), "%s%d_%s%s",
+ hwmon_type_strs[key.type],
+ key.num,
+ hwmon_item_strs[bit],
+ is_alarm ? "_alarm" : "");
+ fd = openat(hwm->hwmon_dir_fd, buf, O_RDONLY);
+ if (fd > 0) {
+ ssize_t read_len = read(fd, buf, sizeof(buf));
+
+ while (read_len > 0 && buf[read_len - 1] == '\n')
+ read_len--;
+
+ if (read_len > 0) {
+ long long val;
+
+ buf[read_len] = '\0';
+ val = strtoll(buf, /*endptr=*/NULL, 10);
+ len += snprintf(out_buf + len, out_buf_len - len, "%s%s%s=%g%s",
+ len == 0 ? " " : ", ",
+ hwmon_item_strs[bit],
+ is_alarm ? "_alarm" : "",
+ (double)val / 1000.0,
+ hwmon_units[key.type]);
+ }
+ close(fd);
+ }
+ }
+ return len;
+}
+
+int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
+{
+ struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
+ struct hashmap_entry *cur;
+ size_t bkt;
+
+ if (hwmon_pmu__read_events(hwm))
+ return false;
+
+ hashmap__for_each_entry((&hwm->events), cur, bkt) {
+ static const char *const hwmon_scale_units[HWMON_TYPE_MAX] = {
+ NULL,
+ "0.001V", /* cpu */
+ "0.001A", /* curr */
+ "0.001J", /* energy */
+ "1rpm", /* fan */
+ "0.001%", /* humidity */
+ "0.001V", /* in */
+ NULL, /* intrusion */
+ "0.001W", /* power */
+ "1Hz", /* pwm */
+ "0.001'C", /* temp */
+ };
+ static const char *const hwmon_desc[HWMON_TYPE_MAX] = {
+ NULL,
+ "CPU core reference voltage", /* cpu */
+ "Current", /* curr */
+ "Cumulative energy use", /* energy */
+ "Fan", /* fan */
+ "Humidity", /* humidity */
+ "Voltage", /* in */
+ "Chassis intrusion detection", /* intrusion */
+ "Power use", /* power */
+ "Pulse width modulation fan control", /* pwm */
+ "Temperature", /* temp */
+ };
+ char alias_buf[64];
+ char desc_buf[256];
+ char encoding_buf[128];
+ union hwmon_pmu_event_key key = {
+ .type_and_num = cur->key,
+ };
+ struct hwmon_pmu_event_value *value = cur->pvalue;
+ struct pmu_event_info info = {
+ .pmu = pmu,
+ .name = value->name,
+ .alias = alias_buf,
+ .scale_unit = hwmon_scale_units[key.type],
+ .desc = desc_buf,
+ .long_desc = NULL,
+ .encoding_desc = encoding_buf,
+ .topic = "hwmon",
+ .pmu_name = pmu->name,
+ .event_type_desc = "Hwmon event",
+ };
+ int ret;
+ size_t len;
+
+ len = snprintf(alias_buf, sizeof(alias_buf), "%s%d",
+ hwmon_type_strs[key.type], key.num);
+ if (!info.name) {
+ info.name = info.alias;
+ info.alias = NULL;
+ }
+
+ len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.",
+ hwmon_desc[key.type],
+ pmu->name + 6,
+ value->label ?: info.name);
+
+ len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len,
+ key, value->items, /*is_alarm=*/false);
+
+ len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len,
+ key, value->alarm_items, /*is_alarm=*/true);
+
+ snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%lx/",
+ pmu->name, cur->key);
+
+ ret = cb(state, &info);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+size_t hwmon_pmu__num_events(struct perf_pmu *pmu)
+{
+ struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
+
+ hwmon_pmu__read_events(hwm);
+ return hashmap__size(&hwm->events);
+}
+
+bool hwmon_pmu__have_event(struct perf_pmu *pmu, const char *name)
+{
+ struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
+ enum hwmon_type type;
+ int number;
+ union hwmon_pmu_event_key key = {};
+ struct hashmap_entry *cur;
+ size_t bkt;
+
+ if (!parse_hwmon_filename(name, &type, &number, /*item=*/NULL, /*is_alarm=*/NULL))
+ return false;
+
+ if (hwmon_pmu__read_events(hwm))
+ return false;
+
+ key.type = type;
+ key.num = number;
+ if (hashmap_find(&hwm->events, key.type_and_num, /*value=*/NULL))
+ return true;
+ if (key.num != -1)
+ return false;
+ /* Item is of form <type>_ which means we should match <type>_<label>. */
+ hashmap__for_each_entry((&hwm->events), cur, bkt) {
+ struct hwmon_pmu_event_value *value = cur->pvalue;
+
+ key.type_and_num = cur->key;
+ if (key.type == type && value->name && !strcasecmp(name, value->name))
+ return true;
+ }
+ return false;
+}
+
+static int hwmon_pmu__config_term(const struct hwmon_pmu *hwm,
+ struct perf_event_attr *attr,
+ struct parse_events_term *term,
+ struct parse_events_error *err)
+{
+ if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
+ enum hwmon_type type;
+ int number;
+
+ if (parse_hwmon_filename(term->config, &type, &number,
+ /*item=*/NULL, /*is_alarm=*/NULL)) {
+ if (number == -1) {
+ /*
+ * Item is of form <type>_ which means we should
+ * match <type>_<label>.
+ */
+ struct hashmap_entry *cur;
+ size_t bkt;
+
+ attr->config = 0;
+ hashmap__for_each_entry((&hwm->events), cur, bkt) {
+ union hwmon_pmu_event_key key = {
+ .type_and_num = cur->key,
+ };
+ struct hwmon_pmu_event_value *value = cur->pvalue;
+
+ if (key.type == type && value->name &&
+ !strcasecmp(term->config, value->name)) {
+ attr->config = key.type_and_num;
+ break;
+ }
+ }
+ if (attr->config == 0)
+ return -EINVAL;
+ } else {
+ union hwmon_pmu_event_key key = {
+ .type = type,
+ .num = number,
+ };
+
+ attr->config = key.type_and_num;
+ }
+ return 0;
+ }
+ }
+ if (err) {
+ char *err_str;
+
+ parse_events_error__handle(err, term->err_val,
+ asprintf(&err_str,
+ "unexpected hwmon event term (%s) %s",
+ parse_events__term_type_str(term->type_term),
+ term->config) < 0
+ ? strdup("unexpected hwmon event term")
+ : err_str,
+ NULL);
+ }
+ return -EINVAL;
+}
+
+int hwmon_pmu__config_terms(const struct perf_pmu *pmu,
+ struct perf_event_attr *attr,
+ struct parse_events_terms *terms,
+ struct parse_events_error *err)
+{
+ const struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
+ struct parse_events_term *term;
+
+ assert(pmu->sysfs_aliases_loaded);
+ list_for_each_entry(term, &terms->terms, list) {
+ if (hwmon_pmu__config_term(hwm, attr, term, err))
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+int hwmon_pmu__check_alias(struct parse_events_terms *terms, struct perf_pmu_info *info,
+ struct parse_events_error *err)
+{
+ struct parse_events_term *term =
+ list_first_entry(&terms->terms, struct parse_events_term, list);
+
+ if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
+ enum hwmon_type type;
+ int number;
+
+ if (parse_hwmon_filename(term->config, &type, &number,
+ /*item=*/NULL, /*is_alarm=*/NULL)) {
+ info->unit = hwmon_units[type];
+ if (type == HWMON_TYPE_FAN || type == HWMON_TYPE_PWM ||
+ type == HWMON_TYPE_INTRUSION)
+ info->scale = 1;
+ else
+ info->scale = 0.001;
+ }
+ return 0;
+ }
+ if (err) {
+ char *err_str;
+
+ parse_events_error__handle(err, term->err_val,
+ asprintf(&err_str,
+ "unexpected hwmon event term (%s) %s",
+ parse_events__term_type_str(term->type_term),
+ term->config) < 0
+ ? strdup("unexpected hwmon event term")
+ : err_str,
+ NULL);
+ }
+ return -EINVAL;
+}
+
+int perf_pmus__read_hwmon_pmus(struct list_head *pmus)
+{
+ char *line = NULL;
+ DIR *class_hwmon_dir;
+ struct dirent *class_hwmon_ent;
+ char buf[PATH_MAX];
+ const char *sysfs = sysfs__mountpoint();
+
+ if (!sysfs)
+ return 0;
+
+ scnprintf(buf, sizeof(buf), "%s/class/hwmon/", sysfs);
+ class_hwmon_dir = opendir(buf);
+ if (!class_hwmon_dir)
+ return 0;
+
+ while ((class_hwmon_ent = readdir(class_hwmon_dir)) != NULL) {
+ size_t line_len;
+ int hwmon_dir, name_fd;
+ struct io io;
+
+ if (class_hwmon_ent->d_type != DT_LNK)
+ continue;
+
+ scnprintf(buf, sizeof(buf), "%s/class/hwmon/%s", sysfs, class_hwmon_ent->d_name);
+ hwmon_dir = open(buf, O_DIRECTORY);
+ if (hwmon_dir == -1) {
+ pr_debug("hwmon_pmu: not a directory: '%s/class/hwmon/%s'\n",
+ sysfs, class_hwmon_ent->d_name);
+ continue;
+ }
+ name_fd = openat(hwmon_dir, "name", O_RDONLY);
+ if (name_fd == -1) {
+ pr_debug("hwmon_pmu: failure to open '%s/class/hwmon/%s/name'\n",
+ sysfs, class_hwmon_ent->d_name);
+ close(hwmon_dir);
+ continue;
+ }
+ io__init(&io, name_fd, buf, sizeof(buf));
+ io__getline(&io, &line, &line_len);
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[line_len - 1] = '\0';
+ hwmon_pmu__new(pmus, hwmon_dir, class_hwmon_ent->d_name, line);
+ close(name_fd);
+ }
+ free(line);
+ closedir(class_hwmon_dir);
+ return 0;
+}
+
+#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
+
+int evsel__hwmon_pmu_open(struct evsel *evsel,
+ struct perf_thread_map *threads,
+ int start_cpu_map_idx, int end_cpu_map_idx)
+{
+ struct hwmon_pmu *hwm = container_of(evsel->pmu, struct hwmon_pmu, pmu);
+ union hwmon_pmu_event_key key = {
+ .type_and_num = evsel->core.attr.config,
+ };
+ int idx = 0, thread = 0, nthreads, err = 0;
+
+ nthreads = perf_thread_map__nr(threads);
+ for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
+ for (thread = 0; thread < nthreads; thread++) {
+ char buf[64];
+ int fd;
+
+ snprintf(buf, sizeof(buf), "%s%d_input",
+ hwmon_type_strs[key.type], key.num);
+
+ fd = openat(hwm->hwmon_dir_fd, buf, O_RDONLY);
+ FD(evsel, idx, thread) = fd;
+ if (fd < 0) {
+ err = -errno;
+ goto out_close;
+ }
+ }
+ }
+ return 0;
+out_close:
+ if (err)
+ threads->err_thread = thread;
+
+ do {
+ while (--thread >= 0) {
+ if (FD(evsel, idx, thread) >= 0)
+ close(FD(evsel, idx, thread));
+ FD(evsel, idx, thread) = -1;
+ }
+ thread = nthreads;
+ } while (--idx >= 0);
+ return err;
+}
+
+int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread)
+{
+ char buf[32];
+ int fd;
+ ssize_t len;
+ struct perf_counts_values *count, *old_count = NULL;
+
+ if (evsel->prev_raw_counts)
+ old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread);
+
+ count = perf_counts(evsel->counts, cpu_map_idx, thread);
+ fd = FD(evsel, cpu_map_idx, thread);
+ len = pread(fd, buf, sizeof(buf), 0);
+ if (len <= 0) {
+ count->lost++;
+ return -EINVAL;
+ }
+ buf[len] = '\0';
+ if (old_count) {
+ count->val = old_count->val + strtoll(buf, NULL, 10);
+ count->run = old_count->run + 1;
+ count->ena = old_count->ena + 1;
+ } else {
+ count->val = strtoll(buf, NULL, 10);
+ count->run++;
+ count->ena++;
+ }
+ return 0;
+}
diff --git a/tools/perf/util/hwmon_pmu.h b/tools/perf/util/hwmon_pmu.h
new file mode 100644
index 000000000000..8061301fcd8e
--- /dev/null
+++ b/tools/perf/util/hwmon_pmu.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __HWMON_PMU_H
+#define __HWMON_PMU_H
+
+#include "pmu.h"
+
+struct list_head;
+
+/**
+ * enum hwmon_type:
+ *
+ * As described in Documentation/hwmon/sysfs-interface.rst hwmon events are
+ * defined over multiple files of the form <type><num>_<item>. This enum
+ * captures potential <type> values.
+ *
+ * This enum is exposed for testing.
+ */
+enum hwmon_type {
+ HWMON_TYPE_NONE,
+
+ HWMON_TYPE_CPU,
+ HWMON_TYPE_CURR,
+ HWMON_TYPE_ENERGY,
+ HWMON_TYPE_FAN,
+ HWMON_TYPE_HUMIDITY,
+ HWMON_TYPE_IN,
+ HWMON_TYPE_INTRUSION,
+ HWMON_TYPE_POWER,
+ HWMON_TYPE_PWM,
+ HWMON_TYPE_TEMP,
+
+ HWMON_TYPE_MAX
+};
+
+/**
+ * enum hwmon_item:
+ *
+ * Similar to enum hwmon_type but describes the item part of a a sysfs filename.
+ *
+ * This enum is exposed for testing.
+ */
+enum hwmon_item {
+ HWMON_ITEM_NONE,
+
+ HWMON_ITEM_ACCURACY,
+ HWMON_ITEM_ALARM,
+ HWMON_ITEM_AUTO_CHANNELS_TEMP,
+ HWMON_ITEM_AVERAGE,
+ HWMON_ITEM_AVERAGE_HIGHEST,
+ HWMON_ITEM_AVERAGE_INTERVAL,
+ HWMON_ITEM_AVERAGE_INTERVAL_MAX,
+ HWMON_ITEM_AVERAGE_INTERVAL_MIN,
+ HWMON_ITEM_AVERAGE_LOWEST,
+ HWMON_ITEM_AVERAGE_MAX,
+ HWMON_ITEM_AVERAGE_MIN,
+ HWMON_ITEM_BEEP,
+ HWMON_ITEM_CAP,
+ HWMON_ITEM_CAP_HYST,
+ HWMON_ITEM_CAP_MAX,
+ HWMON_ITEM_CAP_MIN,
+ HWMON_ITEM_CRIT,
+ HWMON_ITEM_CRIT_HYST,
+ HWMON_ITEM_DIV,
+ HWMON_ITEM_EMERGENCY,
+ HWMON_ITEM_EMERGENCY_HIST,
+ HWMON_ITEM_ENABLE,
+ HWMON_ITEM_FAULT,
+ HWMON_ITEM_FREQ,
+ HWMON_ITEM_HIGHEST,
+ HWMON_ITEM_INPUT,
+ HWMON_ITEM_LABEL,
+ HWMON_ITEM_LCRIT,
+ HWMON_ITEM_LCRIT_HYST,
+ HWMON_ITEM_LOWEST,
+ HWMON_ITEM_MAX,
+ HWMON_ITEM_MAX_HYST,
+ HWMON_ITEM_MIN,
+ HWMON_ITEM_MIN_HYST,
+ HWMON_ITEM_MOD,
+ HWMON_ITEM_OFFSET,
+ HWMON_ITEM_PULSES,
+ HWMON_ITEM_RATED_MAX,
+ HWMON_ITEM_RATED_MIN,
+ HWMON_ITEM_RESET_HISTORY,
+ HWMON_ITEM_TARGET,
+ HWMON_ITEM_TYPE,
+ HWMON_ITEM_VID,
+
+ HWMON_ITEM__MAX,
+};
+
+/** Strings that correspond to enum hwmon_type. */
+extern const char * const hwmon_type_strs[HWMON_TYPE_MAX];
+/** Strings that correspond to enum hwmon_item. */
+extern const char * const hwmon_item_strs[HWMON_ITEM__MAX];
+
+bool perf_pmu__is_hwmon(const struct perf_pmu *pmu);
+bool evsel__is_hwmon(const struct evsel *evsel);
+
+/**
+ * parse_hwmon_filename() - Parse filename into constituent parts.
+ *
+ * @filename: To be parsed, of the form <type><number>_<item>.
+ * @type: The type defined from the parsed file name.
+ * @number: The number of the type, for example there may be more than 1 fan.
+ * @item: A hwmon <type><number> may have multiple associated items.
+ * @alarm: Is the filename for an alarm value?
+ *
+ * An example of a hwmon filename is "temp1_input". The type is temp for a
+ * temperature value. The number is 1. The item within the file is an input
+ * value - the temperature itself. This file doesn't contain an alarm value.
+ *
+ * Exposed for testing.
+ */
+bool parse_hwmon_filename(const char *filename,
+ enum hwmon_type *type,
+ int *number,
+ enum hwmon_item *item,
+ bool *alarm);
+
+/**
+ * hwmon_pmu__new() - Allocate and construct a hwmon PMU.
+ *
+ * @pmus: The list of PMUs to be added to.
+ * @hwmon_dir: An O_DIRECTORY file descriptor for a hwmon directory.
+ * @sysfs_name: Name of the hwmon sysfs directory like hwmon0.
+ * @name: The contents of the "name" file in the hwmon directory.
+ *
+ * Exposed for testing. Regular construction should happen via
+ * perf_pmus__read_hwmon_pmus.
+ */
+struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, int hwmon_dir,
+ const char *sysfs_name, const char *name);
+void hwmon_pmu__exit(struct perf_pmu *pmu);
+
+int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb);
+size_t hwmon_pmu__num_events(struct perf_pmu *pmu);
+bool hwmon_pmu__have_event(struct perf_pmu *pmu, const char *name);
+int hwmon_pmu__config_terms(const struct perf_pmu *pmu,
+ struct perf_event_attr *attr,
+ struct parse_events_terms *terms,
+ struct parse_events_error *err);
+int hwmon_pmu__check_alias(struct parse_events_terms *terms, struct perf_pmu_info *info,
+ struct parse_events_error *err);
+
+int perf_pmus__read_hwmon_pmus(struct list_head *pmus);
+
+
+int evsel__hwmon_pmu_open(struct evsel *evsel,
+ struct perf_thread_map *threads,
+ int start_cpu_map_idx, int end_cpu_map_idx);
+int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread);
+
+#endif /* __HWMON_PMU_H */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index beb93e2603e2..af950f640576 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -18,6 +18,7 @@
#include "debug.h"
#include "evsel.h"
#include "pmu.h"
+#include "hwmon_pmu.h"
#include "pmus.h"
#include "tool_pmu.h"
#include <util/pmu-bison.h>
@@ -1529,6 +1530,9 @@ int perf_pmu__config_terms(const struct perf_pmu *pmu,
{
struct parse_events_term *term;
+ if (perf_pmu__is_hwmon(pmu))
+ return hwmon_pmu__config_terms(pmu, attr, terms, err);
+
list_for_each_entry(term, &terms->terms, list) {
if (pmu_config_term(pmu, attr, term, terms, zero, apply_hardcoded, err))
return -EINVAL;
@@ -1661,6 +1665,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_
info->scale = 0.0;
info->snapshot = false;
+ if (perf_pmu__is_hwmon(pmu)) {
+ ret = hwmon_pmu__check_alias(head_terms, info, err);
+ goto out;
+ }
+
/* Fake PMU doesn't rewrite terms. */
if (perf_pmu__is_fake(pmu))
goto out;
@@ -1830,6 +1839,8 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
return false;
if (perf_pmu__is_tool(pmu) && tool_pmu__skip_event(name))
return false;
+ if (perf_pmu__is_hwmon(pmu))
+ return hwmon_pmu__have_event(pmu, name);
if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
return true;
if (pmu->cpu_aliases_added || !pmu->events_table)
@@ -1841,6 +1852,9 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu)
{
size_t nr;
+ if (perf_pmu__is_hwmon(pmu))
+ return hwmon_pmu__num_events(pmu);
+
pmu_aliases_parse(pmu);
nr = pmu->sysfs_aliases + pmu->sys_json_aliases;
@@ -1904,6 +1918,9 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
int ret = 0;
struct strbuf sb;
+ if (perf_pmu__is_hwmon(pmu))
+ return hwmon_pmu__for_each_event(pmu, state, cb);
+
strbuf_init(&sb, /*hint=*/ 0);
pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu);
@@ -2299,6 +2316,9 @@ int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename,
void perf_pmu__delete(struct perf_pmu *pmu)
{
+ if (perf_pmu__is_hwmon(pmu))
+ hwmon_pmu__exit(pmu);
+
perf_pmu__del_formats(&pmu->format);
perf_pmu__del_aliases(pmu);
perf_pmu__del_caps(pmu);
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index c4ca359d4215..32f95a1060d8 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -37,6 +37,8 @@ struct perf_pmu_caps {
};
enum {
+ PERF_PMU_TYPE_HWMON_START = 0xFFFF0000,
+ PERF_PMU_TYPE_HWMON_END = 0xFFFFFFFD,
PERF_PMU_TYPE_TOOL = 0xFFFFFFFE,
PERF_PMU_TYPE_FAKE = 0xFFFFFFFF,
};
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 107de86c2637..5c3e88adb9e6 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -15,6 +15,7 @@
#include "evsel.h"
#include "pmus.h"
#include "pmu.h"
+#include "hwmon_pmu.h"
#include "tool_pmu.h"
#include "print-events.h"
#include "strbuf.h"
@@ -234,6 +235,7 @@ static void pmu_read_sysfs(bool core_only)
if (!core_only) {
tool_pmu = perf_pmus__tool_pmu();
list_add_tail(&tool_pmu->list, &other_pmus);
+ perf_pmus__read_hwmon_pmus(&other_pmus);
}
list_sort(NULL, &other_pmus, pmus_cmp);
if (!list_empty(&core_pmus)) {
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 12/13] perf test: Add hwmon "PMU" test
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (10 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 11/13] perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 19:03 ` [PATCH v2 13/13] perf docs: Document tool and hwmon events Ian Rogers
2024-09-12 22:50 ` [PATCH v2 00/13] Tool and hwmon PMUs Namhyung Kim
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Based on a mix of the sysfs PMU test (for creating the reference
files) and the tool PMU test, test that parsing given hwmon events
with there aliases creates the expected config values.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/hwmon_pmu.c | 243 ++++++++++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
tools/perf/util/pmus.c | 7 +
tools/perf/util/pmus.h | 3 +
6 files changed, 256 insertions(+)
create mode 100644 tools/perf/tests/hwmon_pmu.c
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index a771e4928247..078d2580d46c 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -67,6 +67,7 @@ perf-test-y += sigtrap.o
perf-test-y += event_groups.o
perf-test-y += symbols.o
perf-test-y += util.o
+perf-test-y += hwmon_pmu.o
perf-test-y += tool_pmu.o
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc))
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 3b30f258c395..81c0f12b8820 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -73,6 +73,7 @@ static struct test_suite *generic_tests[] = {
&suite__PERF_RECORD,
&suite__pmu,
&suite__pmu_events,
+ &suite__hwmon_pmu,
&suite__tool_pmu,
&suite__dso_data,
&suite__perf_evsel__roundtrip_name_test,
diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c
new file mode 100644
index 000000000000..712d6ecbd8f9
--- /dev/null
+++ b/tools/perf/tests/hwmon_pmu.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+#include "debug.h"
+#include "evlist.h"
+#include "parse-events.h"
+#include "tests.h"
+#include "hwmon_pmu.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+
+static const struct test_event {
+ const char *name;
+ const char *alias;
+ long config;
+} test_events[] = {
+ {
+ "temp_test_hwmon_event1",
+ "temp1",
+ 0xA0001,
+ },
+ {
+ "temp_test_hwmon_event2",
+ "temp2",
+ 0xA0002,
+ },
+};
+
+/* Cleanup test PMU directory. */
+static int test_pmu_put(const char *dir, struct perf_pmu *hwm)
+{
+ char buf[PATH_MAX + 20];
+ int ret;
+
+ if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {
+ pr_err("Failure to set up buffer for \"%s\"\n", dir);
+ return -EINVAL;
+ }
+ ret = system(buf);
+ if (ret)
+ pr_err("Failure to \"%s\"\n", buf);
+
+ perf_pmu__delete(hwm);
+ return ret;
+}
+
+/*
+ * Prepare test PMU directory data, normally exported by kernel at
+ * /sys/class/hwmon/hwmon<number>/. Give as input a buffer to hold the file
+ * path, the result is PMU loaded using that directory.
+ */
+static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
+{
+ const char *test_hwmon_name_nl = "A test hwmon PMU\n";
+ const char *test_hwmon_name = "A test hwmon PMU";
+ /* Simulated hwmon items. */
+ const struct test_item {
+ const char *name;
+ const char *value;
+ } test_items[] = {
+ { "temp1_label", "test hwmon event1\n", },
+ { "temp1_input", "40000\n", },
+ { "temp2_label", "test hwmon event2\n", },
+ { "temp2_input", "50000\n", },
+ };
+ int dirfd, file;
+ struct perf_pmu *hwm = NULL;
+ ssize_t len;
+
+ /* Create equivalent of sysfs mount point. */
+ scnprintf(dir, sz, "/tmp/perf-hwmon-pmu-test-XXXXXX");
+ if (!mkdtemp(dir)) {
+ pr_err("mkdtemp failed\n");
+ dir[0] = '\0';
+ return NULL;
+ }
+ dirfd = open(dir, O_DIRECTORY);
+ if (dirfd < 0) {
+ pr_err("Failed to open test directory \"%s\"\n", dir);
+ goto err_out;
+ }
+
+ /* Create the test hwmon directory and give it a name. */
+ if (mkdirat(dirfd, "hwmon1234", 0755) < 0) {
+ pr_err("Failed to mkdir hwmon directory\n");
+ goto err_out;
+ }
+ file = openat(dirfd, "hwmon1234/name", O_WRONLY | O_CREAT, 0600);
+ if (!file) {
+ pr_err("Failed to open for writing file \"name\"\n");
+ goto err_out;
+ }
+ len = strlen(test_hwmon_name_nl);
+ if (write(file, test_hwmon_name_nl, len) < len) {
+ close(file);
+ pr_err("Failed to write to 'name' file\n");
+ goto err_out;
+ }
+ close(file);
+
+ /* Create test hwmon files. */
+ for (size_t i = 0; i < ARRAY_SIZE(test_items); i++) {
+ const struct test_item *item = &test_items[i];
+
+ file = openat(dirfd, item->name, O_WRONLY | O_CREAT, 0600);
+ if (!file) {
+ pr_err("Failed to open for writing file \"%s\"\n", item->name);
+ goto err_out;
+ }
+
+ if (write(file, item->value, strlen(item->value)) < 0) {
+ pr_err("Failed to write to file \"%s\"\n", item->name);
+ close(file);
+ goto err_out;
+ }
+ close(file);
+ }
+
+ /* Make the PMU reading the files created above. */
+ hwm = perf_pmus__add_test_hwmon_pmu(dirfd, "hwmon1234", test_hwmon_name);
+ if (!hwm)
+ pr_err("Test hwmon creation failed\n");
+
+err_out:
+ if (!hwm) {
+ test_pmu_put(dir, hwm);
+ if (dirfd >= 0)
+ close(dirfd);
+ }
+ return hwm;
+}
+
+static int do_test(size_t i, bool with_pmu, bool with_alias)
+{
+ const char *test_event = with_alias ? test_events[i].alias : test_events[i].name;
+ struct evlist *evlist = evlist__new();
+ struct evsel *evsel;
+ struct parse_events_error err;
+ int ret;
+ char str[128];
+ bool found = false;
+
+ if (!evlist) {
+ pr_err("evlist allocation failed\n");
+ return TEST_FAIL;
+ }
+
+ if (with_pmu)
+ snprintf(str, sizeof(str), "/%s/", test_event);
+ else
+ strncpy(str, test_event, sizeof(str));
+
+ pr_debug("Testing '%s'\n", str);
+ parse_events_error__init(&err);
+ ret = parse_events(evlist, str, &err);
+ if (ret) {
+ evlist__delete(evlist);
+
+ pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n",
+ __FILE__, __LINE__, str, ret);
+ parse_events_error__print(&err, str);
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ ret = TEST_OK;
+ if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) {
+ pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n",
+ __FILE__, __LINE__, str, evlist->core.nr_entries);
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ evlist__for_each_entry(evlist, evsel) {
+ if (!perf_pmu__is_hwmon(evsel->pmu))
+ continue;
+
+ if (evsel->core.attr.config != (u64)test_events[i].config) {
+ pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n",
+ __FILE__, __LINE__, str,
+ evsel->core.attr.config,
+ test_events[i].config);
+ ret = TEST_FAIL;
+ goto out;
+ }
+ found = true;
+ }
+
+ if (!found) {
+ pr_debug("FAILED %s:%d Didn't find hwmon event '%s' in parsed evsels\n",
+ __FILE__, __LINE__, str);
+ ret = TEST_FAIL;
+ }
+
+out:
+ evlist__delete(evlist);
+ return ret;
+}
+
+static int test__hwmon_pmu(bool with_pmu)
+{
+ char dir[PATH_MAX];
+ struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
+ int ret = TEST_OK;
+
+ if (!pmu)
+ return TEST_FAIL;
+
+ for (size_t i = 0; i < ARRAY_SIZE(test_events); i++) {
+ ret = do_test(i, with_pmu, /*with_alias=*/false);
+
+ if (ret != TEST_OK)
+ break;
+
+ ret = do_test(i, with_pmu, /*with_alias=*/true);
+
+ if (ret != TEST_OK)
+ break;
+ }
+ test_pmu_put(dir, pmu);
+ return ret;
+}
+
+static int test__hwmon_pmu_without_pmu(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ return test__hwmon_pmu(/*with_pmu=*/false);
+}
+
+static int test__hwmon_pmu_with_pmu(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ return test__hwmon_pmu(/*with_pmu=*/false);
+}
+
+static struct test_case tests__hwmon_pmu[] = {
+ TEST_CASE("Parsing without PMU name", hwmon_pmu_without_pmu),
+ TEST_CASE("Parsing with PMU name", hwmon_pmu_with_pmu),
+ { .name = NULL, }
+};
+
+struct test_suite suite__hwmon_pmu = {
+ .desc = "Hwmon PMU",
+ .test_cases = tests__hwmon_pmu,
+};
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 1ed76d4156b6..260daa77eb06 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -83,6 +83,7 @@ DECLARE_SUITE(perf_evsel__tp_sched_test);
DECLARE_SUITE(syscall_openat_tp_fields);
DECLARE_SUITE(pmu);
DECLARE_SUITE(pmu_events);
+DECLARE_SUITE(hwmon_pmu);
DECLARE_SUITE(tool_pmu);
DECLARE_SUITE(attr);
DECLARE_SUITE(dso_data);
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 5c3e88adb9e6..451c6e00ad70 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -733,6 +733,13 @@ struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name)
return perf_pmu__lookup(&other_pmus, test_sysfs_dirfd, name, /*eager_load=*/true);
}
+struct perf_pmu *perf_pmus__add_test_hwmon_pmu(int hwmon_dir,
+ const char *sysfs_name,
+ const char *name)
+{
+ return hwmon_pmu__new(&other_pmus, hwmon_dir, sysfs_name, name);
+}
+
struct perf_pmu *perf_pmus__fake_pmu(void)
{
static struct perf_pmu fake = {
diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h
index e1742b56eec7..a0cb0eb2ff97 100644
--- a/tools/perf/util/pmus.h
+++ b/tools/perf/util/pmus.h
@@ -30,6 +30,9 @@ bool perf_pmus__supports_extended_type(void);
char *perf_pmus__default_pmu_name(void);
struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name);
+struct perf_pmu *perf_pmus__add_test_hwmon_pmu(int hwmon_dir,
+ const char *sysfs_name,
+ const char *name);
struct perf_pmu *perf_pmus__fake_pmu(void);
#endif /* __PMUS_H */
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v2 13/13] perf docs: Document tool and hwmon events
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (11 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 12/13] perf test: Add hwmon "PMU" test Ian Rogers
@ 2024-09-12 19:03 ` Ian Rogers
2024-09-12 22:50 ` [PATCH v2 00/13] Tool and hwmon PMUs Namhyung Kim
13 siblings, 0 replies; 23+ messages in thread
From: Ian Rogers @ 2024-09-12 19:03 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, John Garry, Will Deacon,
James Clark, Mike Leach, Leo Yan, Ravi Bangoria, Weilin Wang,
Jing Zhang, Xu Yang, Sandipan Das, Benjamin Gray, Athira Jajeev,
Howard Chu, Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
Add a few paragraphs on tool and hwmon events.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Documentation/perf-list.txt | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index dea005410ec0..7e3cd6c5e35d 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -243,6 +243,21 @@ For accessing trace point events perf needs to have read access to
/sys/kernel/tracing, even when perf_event_paranoid is in a relaxed
setting.
+TOOL/HWMON EVENTS
+-----------------
+
+Some events don't have an associated PMU instead reading values
+available to software without perf_event_open. As these events don't
+support sampling they can only really be read by tools like perf stat.
+
+Tool events provide times and certain system parameters. Examples
+include duration_time, user_time, system_time and num_cpus_online.
+
+Hwmon events provide easy access to hwmon sysfs data typically in
+/sys/class/hwmon. This information includes temperatures, fan speeds
+and energy usage.
+
+
TRACING
-------
--
2.46.0.662.g92d0881bb0-goog
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH v2 00/13] Tool and hwmon PMUs
2024-09-12 19:03 [PATCH v2 00/13] Tool and hwmon PMUs Ian Rogers
` (12 preceding siblings ...)
2024-09-12 19:03 ` [PATCH v2 13/13] perf docs: Document tool and hwmon events Ian Rogers
@ 2024-09-12 22:50 ` Namhyung Kim
2024-09-13 14:34 ` Ian Rogers
13 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2024-09-12 22:50 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, John Garry, Will Deacon, James Clark, Mike Leach,
Leo Yan, Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang,
Sandipan Das, Benjamin Gray, Athira Jajeev, Howard Chu,
Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
On Thu, Sep 12, 2024 at 12:03:27PM -0700, Ian Rogers wrote:
> Rather than have fake and tool PMUs being special flags in an evsel,
> create special PMUs. This allows, for example, duration_time to also
> be tool/duration_time/. Once adding events to the tools PMU is just
> adding to an array, add events for nearly all the expr literals like
> num_cpus_online. Rather than create custom logic for finding and
> describing the tool events use json and add a notion of common json
> for the tool events.
>
> Following the convention of the tool PMU, create a hwmon PMU that
> exposes hwmon data for reading. For example, the following shows
> reading the CPU temperature and 2 fan speeds alongside the uncore
> frequency:
> ```
> $ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
> 1.001153138 52.00 'C temp_cpu
> 1.001153138 2,588 rpm fan1
> 1.001153138 2,482 rpm hwmon_thinkpad/fan2/
> 1.001153138 8 tool/num_cpus_online/
> 1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
> 1.001153138 1,012,773,595 duration_time
> ...
> ```
>
> Additional data on the hwmon events is in perf list:
> ```
> $ perf list
> ...
> hwmon:
> ...
> temp_core_0 OR temp2
> [Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
> hwmon_coretemp]
> ...
> ```
>
> v2: Address Namhyung's review feedback. Rebase dropping 4 patches
> applied by Arnaldo, fix build breakage reported by Arnaldo.
>
> Ian Rogers (13):
> perf pmu: Simplify an asprintf error message
> perf pmu: Allow hardcoded terms to be applied to attributes
> perf parse-events: Expose/rename config_term_name
> perf tool_pmu: Factor tool events into their own PMU
> perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
> perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
> perf tool_pmu: Move expr literals to tool_pmu
> perf jevents: Add tool event json under a common architecture
> perf tool_pmu: Switch to standard pmu functions and json descriptions
> perf tests: Add tool PMU test
> perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
> perf test: Add hwmon "PMU" test
> perf docs: Document tool and hwmon events
For patch 1-10,
Acked-by: Namhyung Kim <namhyung@kernel.org>
I'll take a look at hwmon patches later, but I think it'd be nice if you
could split the change into pieces.
Thanks,
Namhyung
>
> tools/perf/Documentation/perf-list.txt | 15 +
> tools/perf/arch/arm64/util/pmu.c | 5 +-
> tools/perf/arch/x86/util/intel-pt.c | 3 +-
> tools/perf/arch/x86/util/tsc.c | 18 +-
> tools/perf/builtin-list.c | 13 +-
> tools/perf/builtin-stat.c | 7 +-
> .../pmu-events/arch/common/common/tool.json | 74 ++
> tools/perf/pmu-events/empty-pmu-events.c | 208 +++--
> tools/perf/pmu-events/jevents.py | 16 +-
> tools/perf/tests/Build | 2 +
> tools/perf/tests/builtin-test.c | 2 +
> tools/perf/tests/hwmon_pmu.c | 243 ++++++
> tools/perf/tests/pmu.c | 3 +-
> tools/perf/tests/tests.h | 2 +
> tools/perf/tests/tool_pmu.c | 111 +++
> tools/perf/util/Build | 2 +
> tools/perf/util/evsel.c | 287 +-----
> tools/perf/util/evsel.h | 28 +-
> tools/perf/util/expr.c | 93 +-
> tools/perf/util/hwmon_pmu.c | 818 ++++++++++++++++++
> tools/perf/util/hwmon_pmu.h | 154 ++++
> tools/perf/util/metricgroup.c | 35 +-
> tools/perf/util/parse-events.c | 62 +-
> tools/perf/util/parse-events.h | 5 +-
> tools/perf/util/parse-events.l | 11 -
> tools/perf/util/parse-events.y | 16 -
> tools/perf/util/pmu.c | 104 ++-
> tools/perf/util/pmu.h | 9 +-
> tools/perf/util/pmus.c | 16 +
> tools/perf/util/pmus.h | 3 +
> tools/perf/util/print-events.c | 36 +-
> tools/perf/util/print-events.h | 1 -
> tools/perf/util/stat-display.c | 14 +-
> tools/perf/util/stat-shadow.c | 22 +-
> tools/perf/util/tool_pmu.c | 508 +++++++++++
> tools/perf/util/tool_pmu.h | 56 ++
> tools/perf/util/tsc.h | 2 +-
> 37 files changed, 2376 insertions(+), 628 deletions(-)
> create mode 100644 tools/perf/pmu-events/arch/common/common/tool.json
> create mode 100644 tools/perf/tests/hwmon_pmu.c
> create mode 100644 tools/perf/tests/tool_pmu.c
> create mode 100644 tools/perf/util/hwmon_pmu.c
> create mode 100644 tools/perf/util/hwmon_pmu.h
> create mode 100644 tools/perf/util/tool_pmu.c
> create mode 100644 tools/perf/util/tool_pmu.h
>
> --
> 2.46.0.662.g92d0881bb0-goog
>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v2 00/13] Tool and hwmon PMUs
2024-09-12 22:50 ` [PATCH v2 00/13] Tool and hwmon PMUs Namhyung Kim
@ 2024-09-13 14:34 ` Ian Rogers
2024-09-26 19:47 ` Ian Rogers
0 siblings, 1 reply; 23+ messages in thread
From: Ian Rogers @ 2024-09-13 14:34 UTC (permalink / raw)
To: Namhyung Kim
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, John Garry, Will Deacon, James Clark, Mike Leach,
Leo Yan, Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang,
Sandipan Das, Benjamin Gray, Athira Jajeev, Howard Chu,
Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
On Thu, Sep 12, 2024 at 3:50 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> On Thu, Sep 12, 2024 at 12:03:27PM -0700, Ian Rogers wrote:
> > Rather than have fake and tool PMUs being special flags in an evsel,
> > create special PMUs. This allows, for example, duration_time to also
> > be tool/duration_time/. Once adding events to the tools PMU is just
> > adding to an array, add events for nearly all the expr literals like
> > num_cpus_online. Rather than create custom logic for finding and
> > describing the tool events use json and add a notion of common json
> > for the tool events.
> >
> > Following the convention of the tool PMU, create a hwmon PMU that
> > exposes hwmon data for reading. For example, the following shows
> > reading the CPU temperature and 2 fan speeds alongside the uncore
> > frequency:
> > ```
> > $ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
> > 1.001153138 52.00 'C temp_cpu
> > 1.001153138 2,588 rpm fan1
> > 1.001153138 2,482 rpm hwmon_thinkpad/fan2/
> > 1.001153138 8 tool/num_cpus_online/
> > 1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
> > 1.001153138 1,012,773,595 duration_time
> > ...
> > ```
> >
> > Additional data on the hwmon events is in perf list:
> > ```
> > $ perf list
> > ...
> > hwmon:
> > ...
> > temp_core_0 OR temp2
> > [Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
> > hwmon_coretemp]
> > ...
> > ```
> >
> > v2: Address Namhyung's review feedback. Rebase dropping 4 patches
> > applied by Arnaldo, fix build breakage reported by Arnaldo.
> >
> > Ian Rogers (13):
> > perf pmu: Simplify an asprintf error message
> > perf pmu: Allow hardcoded terms to be applied to attributes
> > perf parse-events: Expose/rename config_term_name
> > perf tool_pmu: Factor tool events into their own PMU
> > perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
> > perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
> > perf tool_pmu: Move expr literals to tool_pmu
> > perf jevents: Add tool event json under a common architecture
> > perf tool_pmu: Switch to standard pmu functions and json descriptions
> > perf tests: Add tool PMU test
> > perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
> > perf test: Add hwmon "PMU" test
> > perf docs: Document tool and hwmon events
>
> For patch 1-10,
>
> Acked-by: Namhyung Kim <namhyung@kernel.org>
>
> I'll take a look at hwmon patches later, but I think it'd be nice if you
> could split the change into pieces.
Sure, aren't patches pieces? My reason for combining the refactor of
tool events with the addition of hwmon was to motivate the refactor by
a new use-case. I could have done 2 series with the latter dependent
on the first. I'm not sure who wins by 2 patch-set series. There is
extra cognitive load from patches that wouldn't apply cleanly to
tip-of-tree as well as additional work for me.
Thanks,
Ian
> Thanks,
> Namhyung
>
> >
> > tools/perf/Documentation/perf-list.txt | 15 +
> > tools/perf/arch/arm64/util/pmu.c | 5 +-
> > tools/perf/arch/x86/util/intel-pt.c | 3 +-
> > tools/perf/arch/x86/util/tsc.c | 18 +-
> > tools/perf/builtin-list.c | 13 +-
> > tools/perf/builtin-stat.c | 7 +-
> > .../pmu-events/arch/common/common/tool.json | 74 ++
> > tools/perf/pmu-events/empty-pmu-events.c | 208 +++--
> > tools/perf/pmu-events/jevents.py | 16 +-
> > tools/perf/tests/Build | 2 +
> > tools/perf/tests/builtin-test.c | 2 +
> > tools/perf/tests/hwmon_pmu.c | 243 ++++++
> > tools/perf/tests/pmu.c | 3 +-
> > tools/perf/tests/tests.h | 2 +
> > tools/perf/tests/tool_pmu.c | 111 +++
> > tools/perf/util/Build | 2 +
> > tools/perf/util/evsel.c | 287 +-----
> > tools/perf/util/evsel.h | 28 +-
> > tools/perf/util/expr.c | 93 +-
> > tools/perf/util/hwmon_pmu.c | 818 ++++++++++++++++++
> > tools/perf/util/hwmon_pmu.h | 154 ++++
> > tools/perf/util/metricgroup.c | 35 +-
> > tools/perf/util/parse-events.c | 62 +-
> > tools/perf/util/parse-events.h | 5 +-
> > tools/perf/util/parse-events.l | 11 -
> > tools/perf/util/parse-events.y | 16 -
> > tools/perf/util/pmu.c | 104 ++-
> > tools/perf/util/pmu.h | 9 +-
> > tools/perf/util/pmus.c | 16 +
> > tools/perf/util/pmus.h | 3 +
> > tools/perf/util/print-events.c | 36 +-
> > tools/perf/util/print-events.h | 1 -
> > tools/perf/util/stat-display.c | 14 +-
> > tools/perf/util/stat-shadow.c | 22 +-
> > tools/perf/util/tool_pmu.c | 508 +++++++++++
> > tools/perf/util/tool_pmu.h | 56 ++
> > tools/perf/util/tsc.h | 2 +-
> > 37 files changed, 2376 insertions(+), 628 deletions(-)
> > create mode 100644 tools/perf/pmu-events/arch/common/common/tool.json
> > create mode 100644 tools/perf/tests/hwmon_pmu.c
> > create mode 100644 tools/perf/tests/tool_pmu.c
> > create mode 100644 tools/perf/util/hwmon_pmu.c
> > create mode 100644 tools/perf/util/hwmon_pmu.h
> > create mode 100644 tools/perf/util/tool_pmu.c
> > create mode 100644 tools/perf/util/tool_pmu.h
> >
> > --
> > 2.46.0.662.g92d0881bb0-goog
> >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/13] Tool and hwmon PMUs
2024-09-13 14:34 ` Ian Rogers
@ 2024-09-26 19:47 ` Ian Rogers
2024-09-27 17:22 ` Namhyung Kim
0 siblings, 1 reply; 23+ messages in thread
From: Ian Rogers @ 2024-09-26 19:47 UTC (permalink / raw)
To: Namhyung Kim
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, John Garry, Will Deacon, James Clark, Mike Leach,
Leo Yan, Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang,
Sandipan Das, Benjamin Gray, Athira Jajeev, Howard Chu,
Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
On Fri, Sep 13, 2024 at 7:34 AM Ian Rogers <irogers@google.com> wrote:
>
> On Thu, Sep 12, 2024 at 3:50 PM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > On Thu, Sep 12, 2024 at 12:03:27PM -0700, Ian Rogers wrote:
> > > Rather than have fake and tool PMUs being special flags in an evsel,
> > > create special PMUs. This allows, for example, duration_time to also
> > > be tool/duration_time/. Once adding events to the tools PMU is just
> > > adding to an array, add events for nearly all the expr literals like
> > > num_cpus_online. Rather than create custom logic for finding and
> > > describing the tool events use json and add a notion of common json
> > > for the tool events.
> > >
> > > Following the convention of the tool PMU, create a hwmon PMU that
> > > exposes hwmon data for reading. For example, the following shows
> > > reading the CPU temperature and 2 fan speeds alongside the uncore
> > > frequency:
> > > ```
> > > $ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
> > > 1.001153138 52.00 'C temp_cpu
> > > 1.001153138 2,588 rpm fan1
> > > 1.001153138 2,482 rpm hwmon_thinkpad/fan2/
> > > 1.001153138 8 tool/num_cpus_online/
> > > 1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
> > > 1.001153138 1,012,773,595 duration_time
> > > ...
> > > ```
> > >
> > > Additional data on the hwmon events is in perf list:
> > > ```
> > > $ perf list
> > > ...
> > > hwmon:
> > > ...
> > > temp_core_0 OR temp2
> > > [Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
> > > hwmon_coretemp]
> > > ...
> > > ```
> > >
> > > v2: Address Namhyung's review feedback. Rebase dropping 4 patches
> > > applied by Arnaldo, fix build breakage reported by Arnaldo.
> > >
> > > Ian Rogers (13):
> > > perf pmu: Simplify an asprintf error message
> > > perf pmu: Allow hardcoded terms to be applied to attributes
> > > perf parse-events: Expose/rename config_term_name
> > > perf tool_pmu: Factor tool events into their own PMU
> > > perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
> > > perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
> > > perf tool_pmu: Move expr literals to tool_pmu
> > > perf jevents: Add tool event json under a common architecture
> > > perf tool_pmu: Switch to standard pmu functions and json descriptions
> > > perf tests: Add tool PMU test
> > > perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
> > > perf test: Add hwmon "PMU" test
> > > perf docs: Document tool and hwmon events
> >
> > For patch 1-10,
> >
> > Acked-by: Namhyung Kim <namhyung@kernel.org>
I thought the plan was for 1 to 10 to be in v6.12 and the remaining 3
to be in perf-tools-next/v6.13. I'm not seeing any of the series in
perf-tools so should everything be going in perf-tools-next?
Thanks,
Ian
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/13] Tool and hwmon PMUs
2024-09-26 19:47 ` Ian Rogers
@ 2024-09-27 17:22 ` Namhyung Kim
2024-09-27 18:09 ` Ian Rogers
0 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2024-09-27 17:22 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, John Garry, Will Deacon, James Clark, Mike Leach,
Leo Yan, Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang,
Sandipan Das, Benjamin Gray, Athira Jajeev, Howard Chu,
Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
On Thu, Sep 26, 2024 at 12:47:26PM -0700, Ian Rogers wrote:
> On Fri, Sep 13, 2024 at 7:34 AM Ian Rogers <irogers@google.com> wrote:
> >
> > On Thu, Sep 12, 2024 at 3:50 PM Namhyung Kim <namhyung@kernel.org> wrote:
> > >
> > > On Thu, Sep 12, 2024 at 12:03:27PM -0700, Ian Rogers wrote:
> > > > Rather than have fake and tool PMUs being special flags in an evsel,
> > > > create special PMUs. This allows, for example, duration_time to also
> > > > be tool/duration_time/. Once adding events to the tools PMU is just
> > > > adding to an array, add events for nearly all the expr literals like
> > > > num_cpus_online. Rather than create custom logic for finding and
> > > > describing the tool events use json and add a notion of common json
> > > > for the tool events.
> > > >
> > > > Following the convention of the tool PMU, create a hwmon PMU that
> > > > exposes hwmon data for reading. For example, the following shows
> > > > reading the CPU temperature and 2 fan speeds alongside the uncore
> > > > frequency:
> > > > ```
> > > > $ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
> > > > 1.001153138 52.00 'C temp_cpu
> > > > 1.001153138 2,588 rpm fan1
> > > > 1.001153138 2,482 rpm hwmon_thinkpad/fan2/
> > > > 1.001153138 8 tool/num_cpus_online/
> > > > 1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
> > > > 1.001153138 1,012,773,595 duration_time
> > > > ...
> > > > ```
> > > >
> > > > Additional data on the hwmon events is in perf list:
> > > > ```
> > > > $ perf list
> > > > ...
> > > > hwmon:
> > > > ...
> > > > temp_core_0 OR temp2
> > > > [Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
> > > > hwmon_coretemp]
> > > > ...
> > > > ```
> > > >
> > > > v2: Address Namhyung's review feedback. Rebase dropping 4 patches
> > > > applied by Arnaldo, fix build breakage reported by Arnaldo.
> > > >
> > > > Ian Rogers (13):
> > > > perf pmu: Simplify an asprintf error message
> > > > perf pmu: Allow hardcoded terms to be applied to attributes
> > > > perf parse-events: Expose/rename config_term_name
> > > > perf tool_pmu: Factor tool events into their own PMU
> > > > perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
> > > > perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
> > > > perf tool_pmu: Move expr literals to tool_pmu
> > > > perf jevents: Add tool event json under a common architecture
> > > > perf tool_pmu: Switch to standard pmu functions and json descriptions
> > > > perf tests: Add tool PMU test
> > > > perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
> > > > perf test: Add hwmon "PMU" test
> > > > perf docs: Document tool and hwmon events
> > >
> > > For patch 1-10,
> > >
> > > Acked-by: Namhyung Kim <namhyung@kernel.org>
>
> I thought the plan was for 1 to 10 to be in v6.12 and the remaining 3
> to be in perf-tools-next/v6.13. I'm not seeing any of the series in
> perf-tools so should everything be going in perf-tools-next?
Ok, I'll pick up the tools_pmu changes first.
And I think it'd be much easier for me if you break the hwmon change
like with basic PMU enabling and unit/alias support.
Thanks,
Namhyung
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/13] Tool and hwmon PMUs
2024-09-27 17:22 ` Namhyung Kim
@ 2024-09-27 18:09 ` Ian Rogers
2024-09-29 7:21 ` Namhyung Kim
0 siblings, 1 reply; 23+ messages in thread
From: Ian Rogers @ 2024-09-27 18:09 UTC (permalink / raw)
To: Namhyung Kim
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, John Garry, Will Deacon, James Clark, Mike Leach,
Leo Yan, Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang,
Sandipan Das, Benjamin Gray, Athira Jajeev, Howard Chu,
Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
On Fri, Sep 27, 2024 at 10:22 AM Namhyung Kim <namhyung@kernel.org> wrote:
>
> On Thu, Sep 26, 2024 at 12:47:26PM -0700, Ian Rogers wrote:
> > On Fri, Sep 13, 2024 at 7:34 AM Ian Rogers <irogers@google.com> wrote:
> > >
> > > On Thu, Sep 12, 2024 at 3:50 PM Namhyung Kim <namhyung@kernel.org> wrote:
> > > >
> > > > On Thu, Sep 12, 2024 at 12:03:27PM -0700, Ian Rogers wrote:
> > > > > Rather than have fake and tool PMUs being special flags in an evsel,
> > > > > create special PMUs. This allows, for example, duration_time to also
> > > > > be tool/duration_time/. Once adding events to the tools PMU is just
> > > > > adding to an array, add events for nearly all the expr literals like
> > > > > num_cpus_online. Rather than create custom logic for finding and
> > > > > describing the tool events use json and add a notion of common json
> > > > > for the tool events.
> > > > >
> > > > > Following the convention of the tool PMU, create a hwmon PMU that
> > > > > exposes hwmon data for reading. For example, the following shows
> > > > > reading the CPU temperature and 2 fan speeds alongside the uncore
> > > > > frequency:
> > > > > ```
> > > > > $ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
> > > > > 1.001153138 52.00 'C temp_cpu
> > > > > 1.001153138 2,588 rpm fan1
> > > > > 1.001153138 2,482 rpm hwmon_thinkpad/fan2/
> > > > > 1.001153138 8 tool/num_cpus_online/
> > > > > 1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
> > > > > 1.001153138 1,012,773,595 duration_time
> > > > > ...
> > > > > ```
> > > > >
> > > > > Additional data on the hwmon events is in perf list:
> > > > > ```
> > > > > $ perf list
> > > > > ...
> > > > > hwmon:
> > > > > ...
> > > > > temp_core_0 OR temp2
> > > > > [Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
> > > > > hwmon_coretemp]
> > > > > ...
> > > > > ```
> > > > >
> > > > > v2: Address Namhyung's review feedback. Rebase dropping 4 patches
> > > > > applied by Arnaldo, fix build breakage reported by Arnaldo.
> > > > >
> > > > > Ian Rogers (13):
> > > > > perf pmu: Simplify an asprintf error message
> > > > > perf pmu: Allow hardcoded terms to be applied to attributes
> > > > > perf parse-events: Expose/rename config_term_name
> > > > > perf tool_pmu: Factor tool events into their own PMU
> > > > > perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
> > > > > perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
> > > > > perf tool_pmu: Move expr literals to tool_pmu
> > > > > perf jevents: Add tool event json under a common architecture
> > > > > perf tool_pmu: Switch to standard pmu functions and json descriptions
> > > > > perf tests: Add tool PMU test
> > > > > perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
> > > > > perf test: Add hwmon "PMU" test
> > > > > perf docs: Document tool and hwmon events
> > > >
> > > > For patch 1-10,
> > > >
> > > > Acked-by: Namhyung Kim <namhyung@kernel.org>
> >
> > I thought the plan was for 1 to 10 to be in v6.12 and the remaining 3
> > to be in perf-tools-next/v6.13. I'm not seeing any of the series in
> > perf-tools so should everything be going in perf-tools-next?
>
> Ok, I'll pick up the tools_pmu changes first.
>
> And I think it'd be much easier for me if you break the hwmon change
> like with basic PMU enabling and unit/alias support.
I'd kept the hwmon PMU as a single addition on purpose - testing and
documentation are follow up patches. Typically a new driver would be a
single commit, and so I think this is the LKML norm. For example:
https://lore.kernel.org/lkml/20190326151753.19384-3-shameerali.kolothum.thodi@huawei.com/
Having multiple commits where things are only partially working means
bisects will be broken. It also means changes I have on top of this
can end up conflicting with what you're doing. I agree this means we
have a big patch when the new thing is added, I think this is normal
in the case of a driver - which to some extent this is.
Thanks,
Ian
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/13] Tool and hwmon PMUs
2024-09-27 18:09 ` Ian Rogers
@ 2024-09-29 7:21 ` Namhyung Kim
2024-10-01 4:56 ` Ian Rogers
0 siblings, 1 reply; 23+ messages in thread
From: Namhyung Kim @ 2024-09-29 7:21 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, John Garry, Will Deacon, James Clark, Mike Leach,
Leo Yan, Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang,
Sandipan Das, Benjamin Gray, Athira Jajeev, Howard Chu,
Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
On Fri, Sep 27, 2024 at 11:09:49AM -0700, Ian Rogers wrote:
> On Fri, Sep 27, 2024 at 10:22 AM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > On Thu, Sep 26, 2024 at 12:47:26PM -0700, Ian Rogers wrote:
> > > On Fri, Sep 13, 2024 at 7:34 AM Ian Rogers <irogers@google.com> wrote:
> > > >
> > > > On Thu, Sep 12, 2024 at 3:50 PM Namhyung Kim <namhyung@kernel.org> wrote:
> > > > >
> > > > > On Thu, Sep 12, 2024 at 12:03:27PM -0700, Ian Rogers wrote:
> > > > > > Rather than have fake and tool PMUs being special flags in an evsel,
> > > > > > create special PMUs. This allows, for example, duration_time to also
> > > > > > be tool/duration_time/. Once adding events to the tools PMU is just
> > > > > > adding to an array, add events for nearly all the expr literals like
> > > > > > num_cpus_online. Rather than create custom logic for finding and
> > > > > > describing the tool events use json and add a notion of common json
> > > > > > for the tool events.
> > > > > >
> > > > > > Following the convention of the tool PMU, create a hwmon PMU that
> > > > > > exposes hwmon data for reading. For example, the following shows
> > > > > > reading the CPU temperature and 2 fan speeds alongside the uncore
> > > > > > frequency:
> > > > > > ```
> > > > > > $ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
> > > > > > 1.001153138 52.00 'C temp_cpu
> > > > > > 1.001153138 2,588 rpm fan1
> > > > > > 1.001153138 2,482 rpm hwmon_thinkpad/fan2/
> > > > > > 1.001153138 8 tool/num_cpus_online/
> > > > > > 1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
> > > > > > 1.001153138 1,012,773,595 duration_time
> > > > > > ...
> > > > > > ```
> > > > > >
> > > > > > Additional data on the hwmon events is in perf list:
> > > > > > ```
> > > > > > $ perf list
> > > > > > ...
> > > > > > hwmon:
> > > > > > ...
> > > > > > temp_core_0 OR temp2
> > > > > > [Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
> > > > > > hwmon_coretemp]
> > > > > > ...
> > > > > > ```
> > > > > >
> > > > > > v2: Address Namhyung's review feedback. Rebase dropping 4 patches
> > > > > > applied by Arnaldo, fix build breakage reported by Arnaldo.
> > > > > >
> > > > > > Ian Rogers (13):
> > > > > > perf pmu: Simplify an asprintf error message
> > > > > > perf pmu: Allow hardcoded terms to be applied to attributes
> > > > > > perf parse-events: Expose/rename config_term_name
> > > > > > perf tool_pmu: Factor tool events into their own PMU
> > > > > > perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
> > > > > > perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
> > > > > > perf tool_pmu: Move expr literals to tool_pmu
> > > > > > perf jevents: Add tool event json under a common architecture
> > > > > > perf tool_pmu: Switch to standard pmu functions and json descriptions
> > > > > > perf tests: Add tool PMU test
> > > > > > perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
> > > > > > perf test: Add hwmon "PMU" test
> > > > > > perf docs: Document tool and hwmon events
> > > > >
> > > > > For patch 1-10,
> > > > >
> > > > > Acked-by: Namhyung Kim <namhyung@kernel.org>
> > >
> > > I thought the plan was for 1 to 10 to be in v6.12 and the remaining 3
> > > to be in perf-tools-next/v6.13. I'm not seeing any of the series in
> > > perf-tools so should everything be going in perf-tools-next?
> >
> > Ok, I'll pick up the tools_pmu changes first.
It doesn't apply cleanly anymore. Please rebase.
> >
> > And I think it'd be much easier for me if you break the hwmon change
> > like with basic PMU enabling and unit/alias support.
>
> I'd kept the hwmon PMU as a single addition on purpose - testing and
> documentation are follow up patches. Typically a new driver would be a
> single commit, and so I think this is the LKML norm. For example:
> https://lore.kernel.org/lkml/20190326151753.19384-3-shameerali.kolothum.thodi@huawei.com/
>
> Having multiple commits where things are only partially working means
> bisects will be broken. It also means changes I have on top of this
> can end up conflicting with what you're doing. I agree this means we
> have a big patch when the new thing is added, I think this is normal
> in the case of a driver - which to some extent this is.
I think it depends, and of course I want bisectable patches. A
standalone driver with well-known pattern of implementation could come
in a single commit. But this is new and I'm not familiar with the hwmon
interfaces and behaviors. So I'm asking you to split the minimal code
that can run with perf stat from the full-fledged version. That would
help maintainers understand and maintain the code better.
Thanks,
Namhyung
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/13] Tool and hwmon PMUs
2024-09-29 7:21 ` Namhyung Kim
@ 2024-10-01 4:56 ` Ian Rogers
2024-10-01 23:39 ` Namhyung Kim
0 siblings, 1 reply; 23+ messages in thread
From: Ian Rogers @ 2024-10-01 4:56 UTC (permalink / raw)
To: Namhyung Kim
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, John Garry, Will Deacon, James Clark, Mike Leach,
Leo Yan, Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang,
Sandipan Das, Benjamin Gray, Athira Jajeev, Howard Chu,
Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
On Sun, Sep 29, 2024 at 12:21 AM Namhyung Kim <namhyung@kernel.org> wrote:
>
> On Fri, Sep 27, 2024 at 11:09:49AM -0700, Ian Rogers wrote:
> > On Fri, Sep 27, 2024 at 10:22 AM Namhyung Kim <namhyung@kernel.org> wrote:
> > >
> > > On Thu, Sep 26, 2024 at 12:47:26PM -0700, Ian Rogers wrote:
> > > > On Fri, Sep 13, 2024 at 7:34 AM Ian Rogers <irogers@google.com> wrote:
> > > > >
> > > > > On Thu, Sep 12, 2024 at 3:50 PM Namhyung Kim <namhyung@kernel.org> wrote:
> > > > > >
> > > > > > On Thu, Sep 12, 2024 at 12:03:27PM -0700, Ian Rogers wrote:
> > > > > > > Rather than have fake and tool PMUs being special flags in an evsel,
> > > > > > > create special PMUs. This allows, for example, duration_time to also
> > > > > > > be tool/duration_time/. Once adding events to the tools PMU is just
> > > > > > > adding to an array, add events for nearly all the expr literals like
> > > > > > > num_cpus_online. Rather than create custom logic for finding and
> > > > > > > describing the tool events use json and add a notion of common json
> > > > > > > for the tool events.
> > > > > > >
> > > > > > > Following the convention of the tool PMU, create a hwmon PMU that
> > > > > > > exposes hwmon data for reading. For example, the following shows
> > > > > > > reading the CPU temperature and 2 fan speeds alongside the uncore
> > > > > > > frequency:
> > > > > > > ```
> > > > > > > $ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
> > > > > > > 1.001153138 52.00 'C temp_cpu
> > > > > > > 1.001153138 2,588 rpm fan1
> > > > > > > 1.001153138 2,482 rpm hwmon_thinkpad/fan2/
> > > > > > > 1.001153138 8 tool/num_cpus_online/
> > > > > > > 1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
> > > > > > > 1.001153138 1,012,773,595 duration_time
> > > > > > > ...
> > > > > > > ```
> > > > > > >
> > > > > > > Additional data on the hwmon events is in perf list:
> > > > > > > ```
> > > > > > > $ perf list
> > > > > > > ...
> > > > > > > hwmon:
> > > > > > > ...
> > > > > > > temp_core_0 OR temp2
> > > > > > > [Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
> > > > > > > hwmon_coretemp]
> > > > > > > ...
> > > > > > > ```
> > > > > > >
> > > > > > > v2: Address Namhyung's review feedback. Rebase dropping 4 patches
> > > > > > > applied by Arnaldo, fix build breakage reported by Arnaldo.
> > > > > > >
> > > > > > > Ian Rogers (13):
> > > > > > > perf pmu: Simplify an asprintf error message
> > > > > > > perf pmu: Allow hardcoded terms to be applied to attributes
> > > > > > > perf parse-events: Expose/rename config_term_name
> > > > > > > perf tool_pmu: Factor tool events into their own PMU
> > > > > > > perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
> > > > > > > perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
> > > > > > > perf tool_pmu: Move expr literals to tool_pmu
> > > > > > > perf jevents: Add tool event json under a common architecture
> > > > > > > perf tool_pmu: Switch to standard pmu functions and json descriptions
> > > > > > > perf tests: Add tool PMU test
> > > > > > > perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
> > > > > > > perf test: Add hwmon "PMU" test
> > > > > > > perf docs: Document tool and hwmon events
> > > > > >
> > > > > > For patch 1-10,
> > > > > >
> > > > > > Acked-by: Namhyung Kim <namhyung@kernel.org>
> > > >
> > > > I thought the plan was for 1 to 10 to be in v6.12 and the remaining 3
> > > > to be in perf-tools-next/v6.13. I'm not seeing any of the series in
> > > > perf-tools so should everything be going in perf-tools-next?
> > >
> > > Ok, I'll pick up the tools_pmu changes first.
>
> It doesn't apply cleanly anymore. Please rebase.
>
> > >
> > > And I think it'd be much easier for me if you break the hwmon change
> > > like with basic PMU enabling and unit/alias support.
> >
> > I'd kept the hwmon PMU as a single addition on purpose - testing and
> > documentation are follow up patches. Typically a new driver would be a
> > single commit, and so I think this is the LKML norm. For example:
> > https://lore.kernel.org/lkml/20190326151753.19384-3-shameerali.kolothum.thodi@huawei.com/
> >
> > Having multiple commits where things are only partially working means
> > bisects will be broken. It also means changes I have on top of this
> > can end up conflicting with what you're doing. I agree this means we
> > have a big patch when the new thing is added, I think this is normal
> > in the case of a driver - which to some extent this is.
>
> I think it depends, and of course I want bisectable patches. A
> standalone driver with well-known pattern of implementation could come
> in a single commit. But this is new and I'm not familiar with the hwmon
> interfaces and behaviors. So I'm asking you to split the minimal code
> that can run with perf stat from the full-fledged version. That would
> help maintainers understand and maintain the code better.
I consider the code to be in its minimal state. The first 10 patches
lay ground work in tool PMU for an API hwmon PMU will follow, patch 12
adds the testing separated from the main driver and patch 13 adds the
documentation.
In patch 11 the added APIs are:
perf_pmus__read_hwmon_pmus
perf_pmu__is_hwmon
hwmon_pmu__have_event
hwmon_pmu__num_events
hwmon_pmu__for_each_event
hwmon_pmu__check_alias
hwmon_pmu__config_terms
hwmon_pmu__exit
evsel__hwmon_pmu_open
evsel__hwmon_pmu_read
If we read hwmon PMUs without allowing the events to be programmed
then the hwmon code would just be empty and I see no value in such a
patch - I'd be fighting all of the C compilers unused variable and
function warnings.
If we have a hwmon PMU we need the perf_pmu__is_hwmon test as we lack
C++ virtual methods.
There are 3 event functions exactly corresponding to the same
functions in perf_pmu. The reading code store data in a hashmap so
these functions query or iterate the hashmap.
The check_alias and config_terms functions set up the perf_event_attr
and without them the generic code won't work. They are copies of the
generic code but with support for most terms removed as things like
call-graph have no meaning on a hwmon event.
The exit function is the usual house keeping.
The evsel open and read functions are needed as trying to open a
configured event with perf_event_open will fail and break tests like
tools/perf/tests/shell/stat_all_pmu.sh. If you open the event you
can't read it as if the contents were the binary contents of a
perf_event_open file, it has to be read as text so we need the read
function.
The driver is essentially a hashmap. hwmon files are of the form
<type><number>_<item>, so an event is a type and number with the
different items associated with the event held in the hashmap's value.
We can find an event like "temp1" from the type and number but we may
prefer the event "temp_cpu" where "cpu" is read from the label item
file. Finding such labelled events is a linear search and the label
file is read when the event is created in the hashmap for simplicity.
We could make the driver support the "temp1" name and then the
"temp_cpu" name but the line savings would be minimal, we'd have a new
hwmon driver that would have different rules around event alias names
and the like, basically it'd be a whole heap of work that would in the
next patch just be thrown away.
I've described how the driver works in the paragraph above but in the
code I added a generous amount of kerneldoc that already says this. I
could remove the kerneldoc and add it as an additional patch, but I
don't see why. I also don't see how this differs from how existing
drivers are added except that the pmu API is cleaned up prior to use
while drivers tend to be using a stable API.
Thanks,
Ian
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/13] Tool and hwmon PMUs
2024-10-01 4:56 ` Ian Rogers
@ 2024-10-01 23:39 ` Namhyung Kim
0 siblings, 0 replies; 23+ messages in thread
From: Namhyung Kim @ 2024-10-01 23:39 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, John Garry, Will Deacon, James Clark, Mike Leach,
Leo Yan, Ravi Bangoria, Weilin Wang, Jing Zhang, Xu Yang,
Sandipan Das, Benjamin Gray, Athira Jajeev, Howard Chu,
Dominique Martinet, Yang Jihong, Colin Ian King,
Veronika Molnarova, Dr. David Alan Gilbert, Oliver Upton,
Changbin Du, Ze Gao, Andi Kleen, Clément Le Goffic,
Sun Haiyong, Junhao He, Tiezhu Yang, Yicong Yang, linux-kernel,
linux-perf-users, linux-arm-kernel
On Mon, Sep 30, 2024 at 09:56:29PM -0700, Ian Rogers wrote:
> On Sun, Sep 29, 2024 at 12:21 AM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > On Fri, Sep 27, 2024 at 11:09:49AM -0700, Ian Rogers wrote:
> > > On Fri, Sep 27, 2024 at 10:22 AM Namhyung Kim <namhyung@kernel.org> wrote:
> > > >
> > > > On Thu, Sep 26, 2024 at 12:47:26PM -0700, Ian Rogers wrote:
> > > > > On Fri, Sep 13, 2024 at 7:34 AM Ian Rogers <irogers@google.com> wrote:
> > > > > >
> > > > > > On Thu, Sep 12, 2024 at 3:50 PM Namhyung Kim <namhyung@kernel.org> wrote:
> > > > > > >
> > > > > > > On Thu, Sep 12, 2024 at 12:03:27PM -0700, Ian Rogers wrote:
> > > > > > > > Rather than have fake and tool PMUs being special flags in an evsel,
> > > > > > > > create special PMUs. This allows, for example, duration_time to also
> > > > > > > > be tool/duration_time/. Once adding events to the tools PMU is just
> > > > > > > > adding to an array, add events for nearly all the expr literals like
> > > > > > > > num_cpus_online. Rather than create custom logic for finding and
> > > > > > > > describing the tool events use json and add a notion of common json
> > > > > > > > for the tool events.
> > > > > > > >
> > > > > > > > Following the convention of the tool PMU, create a hwmon PMU that
> > > > > > > > exposes hwmon data for reading. For example, the following shows
> > > > > > > > reading the CPU temperature and 2 fan speeds alongside the uncore
> > > > > > > > frequency:
> > > > > > > > ```
> > > > > > > > $ perf stat -e temp_cpu,fan1,hwmon_thinkpad/fan2/,tool/num_cpus_online/ -M UNCORE_FREQ -I 1000
> > > > > > > > 1.001153138 52.00 'C temp_cpu
> > > > > > > > 1.001153138 2,588 rpm fan1
> > > > > > > > 1.001153138 2,482 rpm hwmon_thinkpad/fan2/
> > > > > > > > 1.001153138 8 tool/num_cpus_online/
> > > > > > > > 1.001153138 1,077,101,397 UNC_CLOCK.SOCKET # 1.08 UNCORE_FREQ
> > > > > > > > 1.001153138 1,012,773,595 duration_time
> > > > > > > > ...
> > > > > > > > ```
> > > > > > > >
> > > > > > > > Additional data on the hwmon events is in perf list:
> > > > > > > > ```
> > > > > > > > $ perf list
> > > > > > > > ...
> > > > > > > > hwmon:
> > > > > > > > ...
> > > > > > > > temp_core_0 OR temp2
> > > > > > > > [Temperature in unit coretemp named Core 0. crit=100'C,max=100'C crit_alarm=0'C. Unit:
> > > > > > > > hwmon_coretemp]
> > > > > > > > ...
> > > > > > > > ```
> > > > > > > >
> > > > > > > > v2: Address Namhyung's review feedback. Rebase dropping 4 patches
> > > > > > > > applied by Arnaldo, fix build breakage reported by Arnaldo.
> > > > > > > >
> > > > > > > > Ian Rogers (13):
> > > > > > > > perf pmu: Simplify an asprintf error message
> > > > > > > > perf pmu: Allow hardcoded terms to be applied to attributes
> > > > > > > > perf parse-events: Expose/rename config_term_name
> > > > > > > > perf tool_pmu: Factor tool events into their own PMU
> > > > > > > > perf tool_pmu: Rename enum perf_tool_event to tool_pmu_event
> > > > > > > > perf tool_pmu: Rename perf_tool_event__* to tool_pmu__*
> > > > > > > > perf tool_pmu: Move expr literals to tool_pmu
> > > > > > > > perf jevents: Add tool event json under a common architecture
> > > > > > > > perf tool_pmu: Switch to standard pmu functions and json descriptions
> > > > > > > > perf tests: Add tool PMU test
> > > > > > > > perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs
> > > > > > > > perf test: Add hwmon "PMU" test
> > > > > > > > perf docs: Document tool and hwmon events
> > > > > > >
> > > > > > > For patch 1-10,
> > > > > > >
> > > > > > > Acked-by: Namhyung Kim <namhyung@kernel.org>
> > > > >
> > > > > I thought the plan was for 1 to 10 to be in v6.12 and the remaining 3
> > > > > to be in perf-tools-next/v6.13. I'm not seeing any of the series in
> > > > > perf-tools so should everything be going in perf-tools-next?
> > > >
> > > > Ok, I'll pick up the tools_pmu changes first.
> >
> > It doesn't apply cleanly anymore. Please rebase.
> >
> > > >
> > > > And I think it'd be much easier for me if you break the hwmon change
> > > > like with basic PMU enabling and unit/alias support.
> > >
> > > I'd kept the hwmon PMU as a single addition on purpose - testing and
> > > documentation are follow up patches. Typically a new driver would be a
> > > single commit, and so I think this is the LKML norm. For example:
> > > https://lore.kernel.org/lkml/20190326151753.19384-3-shameerali.kolothum.thodi@huawei.com/
> > >
> > > Having multiple commits where things are only partially working means
> > > bisects will be broken. It also means changes I have on top of this
> > > can end up conflicting with what you're doing. I agree this means we
> > > have a big patch when the new thing is added, I think this is normal
> > > in the case of a driver - which to some extent this is.
> >
> > I think it depends, and of course I want bisectable patches. A
> > standalone driver with well-known pattern of implementation could come
> > in a single commit. But this is new and I'm not familiar with the hwmon
> > interfaces and behaviors. So I'm asking you to split the minimal code
> > that can run with perf stat from the full-fledged version. That would
> > help maintainers understand and maintain the code better.
>
> I consider the code to be in its minimal state. The first 10 patches
> lay ground work in tool PMU for an API hwmon PMU will follow, patch 12
Yes, please send the first 10 patches separately.
> adds the testing separated from the main driver and patch 13 adds the
> documentation.
>
> In patch 11 the added APIs are:
> perf_pmus__read_hwmon_pmus
> perf_pmu__is_hwmon
> hwmon_pmu__have_event
> hwmon_pmu__num_events
> hwmon_pmu__for_each_event
> hwmon_pmu__check_alias
> hwmon_pmu__config_terms
> hwmon_pmu__exit
> evsel__hwmon_pmu_open
> evsel__hwmon_pmu_read
>
> If we read hwmon PMUs without allowing the events to be programmed
> then the hwmon code would just be empty and I see no value in such a
> patch - I'd be fighting all of the C compilers unused variable and
> function warnings.
I'd like to have a minimal working version first and then add more
features or convenience on and on. It should read *some* numbers
from hwmon.
> If we have a hwmon PMU we need the perf_pmu__is_hwmon test as we lack
> C++ virtual methods.
> There are 3 event functions exactly corresponding to the same
> functions in perf_pmu. The reading code store data in a hashmap so
> these functions query or iterate the hashmap.
> The check_alias and config_terms functions set up the perf_event_attr
> and without them the generic code won't work. They are copies of the
> generic code but with support for most terms removed as things like
> call-graph have no meaning on a hwmon event.
Can the check_alias() part be optional? I'm not sure how much work is
needed in config_terms() other than setting attr.type for the PMU in the
minimal version.
> The exit function is the usual house keeping.
> The evsel open and read functions are needed as trying to open a
> configured event with perf_event_open will fail and break tests like
> tools/perf/tests/shell/stat_all_pmu.sh. If you open the event you
> can't read it as if the contents were the binary contents of a
> perf_event_open file, it has to be read as text so we need the read
> function.
Yep, these are essential.
>
> The driver is essentially a hashmap. hwmon files are of the form
> <type><number>_<item>, so an event is a type and number with the
> different items associated with the event held in the hashmap's value.
> We can find an event like "temp1" from the type and number but we may
> prefer the event "temp_cpu" where "cpu" is read from the label item
> file. Finding such labelled events is a linear search and the label
> file is read when the event is created in the hashmap for simplicity.
I guess this can be in the second patch to improve the behavior. By
reading the label, it can give the meaning of the numbers in the hwmon.
But it could work without that too, right?
> We could make the driver support the "temp1" name and then the
> "temp_cpu" name but the line savings would be minimal, we'd have a new
> hwmon driver that would have different rules around event alias names
> and the like, basically it'd be a whole heap of work that would in the
> next patch just be thrown away.
Pleaes keep it minimal to avoid unnecessary work as much as possible.
I think it's needed for maintainers and other contributors to
understand the code and the semantics better.
>
> I've described how the driver works in the paragraph above but in the
> code I added a generous amount of kerneldoc that already says this. I
> could remove the kerneldoc and add it as an additional patch, but I
> don't see why. I also don't see how this differs from how existing
> drivers are added except that the pmu API is cleaned up prior to use
> while drivers tend to be using a stable API.
I appreciate your work on this.
Thanks,
Namhyung
^ permalink raw reply [flat|nested] 23+ messages in thread