Commit 3241d46f authored by Ian Rogers's avatar Ian Rogers Committed by Namhyung Kim

perf pmus: Sort/merge/aggregate PMUs like mrvl_ddr_pmu

The mrvl_ddr_pmu is uncore and has a hexadecimal address suffix while
the previous PMU sorting/merging code assumes uncore PMU names start
with uncore_ and have a decimal suffix. Because of the previous
assumption it isn't possible to wildcard the mrvl_ddr_pmu.

Modify pmu_name_len_no_suffix but also remove the suffix number out
argument, this is because we don't know if a suffix number of say 100
is in hexadecimal or decimal. As the only use of the suffix number is
in comparisons, it is safe there to compare the values as hexadecimal.
Modify perf_pmu__match_ignoring_suffix so that hexadecimal suffixes
are ignored.

Only allow hexadecimal suffixes to be greater than length 2 (ie 3 or
more) so that S390's cpum_cf PMU doesn't lose its suffix.

Change the return type of pmu_name_len_no_suffix to size_t to
workaround GCC incorrectly determining the result could be negative.
Signed-off-by: default avatarIan Rogers <irogers@google.com>
Acked-by: default avatarNamhyung Kim <namhyung@kernel.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: James Clark <james.clark@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Will Deacon <will@kernel.org>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Bharat Bhushan <bbhushan2@marvell.com>
Cc: Bhaskara Budiredla <bbudiredla@marvell.com>
Cc: Tuan Phan <tuanphan@os.amperecomputing.com>
Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20240515060114.3268149-2-irogers@google.com
parent 1613e604
...@@ -856,26 +856,34 @@ __weak const struct pmu_metrics_table *pmu_metrics_table__find(void) ...@@ -856,26 +856,34 @@ __weak const struct pmu_metrics_table *pmu_metrics_table__find(void)
*/ */
static bool perf_pmu__match_ignoring_suffix(const char *pmu_name, const char *tok) static bool perf_pmu__match_ignoring_suffix(const char *pmu_name, const char *tok)
{ {
const char *p; const char *p, *suffix;
bool has_hex = false;
if (strncmp(pmu_name, tok, strlen(tok))) if (strncmp(pmu_name, tok, strlen(tok)))
return false; return false;
p = pmu_name + strlen(tok); suffix = p = pmu_name + strlen(tok);
if (*p == 0) if (*p == 0)
return true; return true;
if (*p == '_') if (*p == '_') {
++p; ++p;
++suffix;
}
/* Ensure we end in a number */ /* Ensure we end in a number */
while (1) { while (1) {
if (!isdigit(*p)) if (!isxdigit(*p))
return false; return false;
if (!has_hex)
has_hex = !isdigit(*p);
if (*(++p) == 0) if (*(++p) == 0)
break; break;
} }
if (has_hex)
return (p - suffix) > 2;
return true; return true;
} }
...@@ -1788,10 +1796,10 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, ...@@ -1788,10 +1796,10 @@ static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
const struct perf_pmu_alias *alias, bool skip_duplicate_pmus) const struct perf_pmu_alias *alias, bool skip_duplicate_pmus)
{ {
struct parse_events_term *term; struct parse_events_term *term;
int pmu_name_len = skip_duplicate_pmus size_t pmu_name_len = skip_duplicate_pmus
? pmu_name_len_no_suffix(pmu->name, /*num=*/NULL) ? pmu_name_len_no_suffix(pmu->name)
: (int)strlen(pmu->name); : strlen(pmu->name);
int used = snprintf(buf, len, "%.*s/%s", pmu_name_len, pmu->name, alias->name); int used = snprintf(buf, len, "%.*s/%s", (int)pmu_name_len, pmu->name, alias->name);
list_for_each_entry(term, &alias->terms.terms, list) { list_for_each_entry(term, &alias->terms.terms, list) {
if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
...@@ -1828,13 +1836,12 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, ...@@ -1828,13 +1836,12 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
pmu_aliases_parse(pmu); pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu); pmu_add_cpu_aliases(pmu);
list_for_each_entry(event, &pmu->aliases, list) { list_for_each_entry(event, &pmu->aliases, list) {
size_t buf_used; size_t buf_used, pmu_name_len;
int pmu_name_len;
info.pmu_name = event->pmu_name ?: pmu->name; info.pmu_name = event->pmu_name ?: pmu->name;
pmu_name_len = skip_duplicate_pmus pmu_name_len = skip_duplicate_pmus
? pmu_name_len_no_suffix(info.pmu_name, /*num=*/NULL) ? pmu_name_len_no_suffix(info.pmu_name)
: (int)strlen(info.pmu_name); : strlen(info.pmu_name);
info.alias = NULL; info.alias = NULL;
if (event->desc) { if (event->desc) {
info.name = event->name; info.name = event->name;
...@@ -1859,7 +1866,7 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, ...@@ -1859,7 +1866,7 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
info.encoding_desc = buf + buf_used; info.encoding_desc = buf + buf_used;
parse_events_terms__to_strbuf(&event->terms, &sb); parse_events_terms__to_strbuf(&event->terms, &sb);
buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
"%.*s/%s/", pmu_name_len, info.pmu_name, sb.buf) + 1; "%.*s/%s/", (int)pmu_name_len, info.pmu_name, sb.buf) + 1;
info.topic = event->topic; info.topic = event->topic;
info.str = sb.buf; info.str = sb.buf;
info.deprecated = event->deprecated; info.deprecated = event->deprecated;
......
...@@ -40,31 +40,52 @@ static bool read_sysfs_all_pmus; ...@@ -40,31 +40,52 @@ static bool read_sysfs_all_pmus;
static void pmu_read_sysfs(bool core_only); static void pmu_read_sysfs(bool core_only);
int pmu_name_len_no_suffix(const char *str, unsigned long *num) size_t pmu_name_len_no_suffix(const char *str)
{ {
int orig_len, len; int orig_len, len;
bool has_hex_digits = false;
orig_len = len = strlen(str); orig_len = len = strlen(str);
/* Non-uncore PMUs have their full length, for example, i915. */ /* Count trailing digits. */
if (!strstarts(str, "uncore_")) while (len > 0 && isxdigit(str[len - 1])) {
return len; if (!isdigit(str[len - 1]))
has_hex_digits = true;
/*
* Count trailing digits and '_', if '_{num}' suffix isn't present use
* the full length.
*/
while (len > 0 && isdigit(str[len - 1]))
len--; len--;
}
if (len > 0 && len != orig_len && str[len - 1] == '_') { if (len > 0 && len != orig_len && str[len - 1] == '_') {
if (num) /*
*num = strtoul(&str[len], NULL, 10); * There is a '_{num}' suffix. For decimal suffixes any length
return len - 1; * will do, for hexadecimal ensure more than 2 hex digits so
* that S390's cpum_cf PMU doesn't match.
*/
if (!has_hex_digits || (orig_len - len) > 2)
return len - 1;
} }
/* Use the full length. */
return orig_len; return orig_len;
} }
int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name)
{
unsigned long lhs_num = 0, rhs_num = 0;
size_t lhs_pmu_name_len = pmu_name_len_no_suffix(lhs_pmu_name);
size_t rhs_pmu_name_len = pmu_name_len_no_suffix(rhs_pmu_name);
int ret = strncmp(lhs_pmu_name, rhs_pmu_name,
lhs_pmu_name_len < rhs_pmu_name_len ? lhs_pmu_name_len : rhs_pmu_name_len);
if (lhs_pmu_name_len != rhs_pmu_name_len || ret != 0 || lhs_pmu_name_len == 0)
return ret;
if (lhs_pmu_name_len + 1 < strlen(lhs_pmu_name))
lhs_num = strtoul(&lhs_pmu_name[lhs_pmu_name_len + 1], NULL, 16);
if (rhs_pmu_name_len + 1 < strlen(rhs_pmu_name))
rhs_num = strtoul(&rhs_pmu_name[rhs_pmu_name_len + 1], NULL, 16);
return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0);
}
void perf_pmus__destroy(void) void perf_pmus__destroy(void)
{ {
struct perf_pmu *pmu, *tmp; struct perf_pmu *pmu, *tmp;
...@@ -167,20 +188,10 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) ...@@ -167,20 +188,10 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
static int pmus_cmp(void *priv __maybe_unused, static int pmus_cmp(void *priv __maybe_unused,
const struct list_head *lhs, const struct list_head *rhs) const struct list_head *lhs, const struct list_head *rhs)
{ {
unsigned long lhs_num = 0, rhs_num = 0;
struct perf_pmu *lhs_pmu = container_of(lhs, struct perf_pmu, list); struct perf_pmu *lhs_pmu = container_of(lhs, struct perf_pmu, list);
struct perf_pmu *rhs_pmu = container_of(rhs, struct perf_pmu, list); struct perf_pmu *rhs_pmu = container_of(rhs, struct perf_pmu, list);
const char *lhs_pmu_name = lhs_pmu->name ?: "";
const char *rhs_pmu_name = rhs_pmu->name ?: "";
int lhs_pmu_name_len = pmu_name_len_no_suffix(lhs_pmu_name, &lhs_num);
int rhs_pmu_name_len = pmu_name_len_no_suffix(rhs_pmu_name, &rhs_num);
int ret = strncmp(lhs_pmu_name, rhs_pmu_name,
lhs_pmu_name_len < rhs_pmu_name_len ? lhs_pmu_name_len : rhs_pmu_name_len);
if (lhs_pmu_name_len != rhs_pmu_name_len || ret != 0 || lhs_pmu_name_len == 0)
return ret;
return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0); return pmu_name_cmp(lhs_pmu->name ?: "", rhs_pmu->name ?: "");
} }
/* Add all pmus in sysfs to pmu list: */ /* Add all pmus in sysfs to pmu list: */
...@@ -300,11 +311,11 @@ static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu) ...@@ -300,11 +311,11 @@ static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu)
pmu_read_sysfs(/*core_only=*/false); pmu_read_sysfs(/*core_only=*/false);
pmu = list_prepare_entry(pmu, &core_pmus, list); pmu = list_prepare_entry(pmu, &core_pmus, list);
} else } else
last_pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", NULL); last_pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");
if (use_core_pmus) { if (use_core_pmus) {
list_for_each_entry_continue(pmu, &core_pmus, list) { list_for_each_entry_continue(pmu, &core_pmus, list) {
int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", /*num=*/NULL); int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");
if (last_pmu_name_len == pmu_name_len && if (last_pmu_name_len == pmu_name_len &&
!strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len)) !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len))
...@@ -316,7 +327,7 @@ static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu) ...@@ -316,7 +327,7 @@ static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu)
pmu = list_prepare_entry(pmu, &other_pmus, list); pmu = list_prepare_entry(pmu, &other_pmus, list);
} }
list_for_each_entry_continue(pmu, &other_pmus, list) { list_for_each_entry_continue(pmu, &other_pmus, list) {
int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", /*num=*/NULL); int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "");
if (last_pmu_name_len == pmu_name_len && if (last_pmu_name_len == pmu_name_len &&
!strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len)) !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len))
...@@ -566,7 +577,7 @@ void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, voi ...@@ -566,7 +577,7 @@ void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, voi
.long_string = STRBUF_INIT, .long_string = STRBUF_INIT,
.num_formats = 0, .num_formats = 0,
}; };
int len = pmu_name_len_no_suffix(pmu->name, /*num=*/NULL); 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)"; const char *desc = "(see 'man perf-list' or 'man perf-record' on how to encode it)";
if (!pmu->is_core) if (!pmu->is_core)
......
...@@ -2,10 +2,15 @@ ...@@ -2,10 +2,15 @@
#ifndef __PMUS_H #ifndef __PMUS_H
#define __PMUS_H #define __PMUS_H
#include <stdbool.h>
#include <stddef.h>
struct perf_pmu; struct perf_pmu;
struct print_callbacks; struct print_callbacks;
int pmu_name_len_no_suffix(const char *str, unsigned long *num); size_t pmu_name_len_no_suffix(const char *str);
/* Exposed for testing only. */
int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name);
void perf_pmus__destroy(void); void perf_pmus__destroy(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment