summaryrefslogtreecommitdiff
path: root/tools/perf/util/pmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/pmu.c')
-rw-r--r--tools/perf/util/pmu.c291
1 files changed, 229 insertions, 62 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index f39cbbc1a7ec..888ce9912275 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -36,6 +36,18 @@ struct perf_pmu perf_pmu__fake = {
#define UNIT_MAX_LEN 31 /* max length for event unit name */
+enum event_source {
+ /* An event loaded from /sys/devices/<pmu>/events. */
+ EVENT_SRC_SYSFS,
+ /* An event loaded from a CPUID matched json file. */
+ EVENT_SRC_CPU_JSON,
+ /*
+ * An event loaded from a /sys/devices/<pmu>/identifier matched json
+ * file.
+ */
+ EVENT_SRC_SYS_JSON,
+};
+
/**
* struct perf_pmu_alias - An event either read from sysfs or builtin in
* pmu-events.c, created by parsing the pmu-events json files.
@@ -182,7 +194,7 @@ static void perf_pmu_format__load(const struct perf_pmu *pmu, struct perf_pmu_fo
* Parse & process all the sysfs attributes located under
* the directory specified in 'dir' parameter.
*/
-int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load)
+static int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load)
{
struct dirent *evt_ent;
DIR *format_dir;
@@ -232,7 +244,7 @@ int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load)
* located at:
* /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
*/
-static int pmu_format(struct perf_pmu *pmu, int dirfd, const char *name)
+static int pmu_format(struct perf_pmu *pmu, int dirfd, const char *name, bool eager_load)
{
int fd;
@@ -241,7 +253,7 @@ static int pmu_format(struct perf_pmu *pmu, int dirfd, const char *name)
return 0;
/* it'll close the fd */
- if (perf_pmu__format_parse(pmu, fd, /*eager_load=*/false))
+ if (perf_pmu__format_parse(pmu, fd, eager_load))
return -1;
return 0;
@@ -425,9 +437,30 @@ static struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu,
{
struct perf_pmu_alias *alias;
- if (load && !pmu->sysfs_aliases_loaded)
- pmu_aliases_parse(pmu);
+ if (load && !pmu->sysfs_aliases_loaded) {
+ bool has_sysfs_event;
+ char event_file_name[FILENAME_MAX + 8];
+
+ /*
+ * Test if alias/event 'name' exists in the PMU's sysfs/events
+ * directory. If not skip parsing the sysfs aliases. Sysfs event
+ * name must be all lower or all upper case.
+ */
+ scnprintf(event_file_name, sizeof(event_file_name), "events/%s", name);
+ for (size_t i = 7, n = 7 + strlen(name); i < n; i++)
+ event_file_name[i] = tolower(event_file_name[i]);
+
+ has_sysfs_event = perf_pmu__file_exists(pmu, event_file_name);
+ if (!has_sysfs_event) {
+ for (size_t i = 7, n = 7 + strlen(name); i < n; i++)
+ event_file_name[i] = toupper(event_file_name[i]);
+
+ has_sysfs_event = perf_pmu__file_exists(pmu, event_file_name);
+ }
+ if (has_sysfs_event)
+ pmu_aliases_parse(pmu);
+ }
list_for_each_entry(alias, &pmu->aliases, list) {
if (!strcasecmp(alias->name, name))
return alias;
@@ -500,7 +533,7 @@ static int update_alias(const struct pmu_event *pe,
static int perf_pmu__new_alias(struct perf_pmu *pmu, const char *name,
const char *desc, const char *val, FILE *val_fd,
- const struct pmu_event *pe)
+ const struct pmu_event *pe, enum event_source src)
{
struct perf_pmu_alias *alias;
int ret;
@@ -518,7 +551,8 @@ static int perf_pmu__new_alias(struct perf_pmu *pmu, const char *name,
unit = pe->unit;
perpkg = pe->perpkg;
deprecated = pe->deprecated;
- pmu_name = pe->pmu;
+ if (pe->pmu && strcmp(pe->pmu, "default_core"))
+ pmu_name = pe->pmu;
}
alias = zalloc(sizeof(*alias));
@@ -552,25 +586,30 @@ static int perf_pmu__new_alias(struct perf_pmu *pmu, const char *name,
}
snprintf(alias->unit, sizeof(alias->unit), "%s", unit);
}
- if (!pe) {
- /* Update an event from sysfs with json data. */
- struct update_alias_data data = {
- .pmu = pmu,
- .alias = alias,
- };
-
+ switch (src) {
+ default:
+ case EVENT_SRC_SYSFS:
alias->from_sysfs = true;
if (pmu->events_table) {
+ /* Update an event from sysfs with json data. */
+ struct update_alias_data data = {
+ .pmu = pmu,
+ .alias = alias,
+ };
if (pmu_events_table__find_event(pmu->events_table, pmu, name,
update_alias, &data) == 0)
- pmu->loaded_json_aliases++;
+ pmu->cpu_json_aliases++;
}
- }
-
- if (!pe)
pmu->sysfs_aliases++;
- else
- pmu->loaded_json_aliases++;
+ break;
+ case EVENT_SRC_CPU_JSON:
+ pmu->cpu_json_aliases++;
+ break;
+ case EVENT_SRC_SYS_JSON:
+ pmu->sys_json_aliases++;
+ break;
+
+ }
list_add_tail(&alias->list, &pmu->aliases);
return 0;
}
@@ -596,33 +635,18 @@ static inline bool pmu_alias_info_file(const char *name)
* Reading the pmu event aliases definition, which should be located at:
* /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
*/
-static int pmu_aliases_parse(struct perf_pmu *pmu)
+static int __pmu_aliases_parse(struct perf_pmu *pmu, int events_dir_fd)
{
- char path[PATH_MAX];
struct dirent *evt_ent;
DIR *event_dir;
- size_t len;
- int fd, dir_fd;
- len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
- if (!len)
- return 0;
- scnprintf(path + len, sizeof(path) - len, "%s/events", pmu->name);
-
- dir_fd = open(path, O_DIRECTORY);
- if (dir_fd == -1) {
- pmu->sysfs_aliases_loaded = true;
- return 0;
- }
-
- event_dir = fdopendir(dir_fd);
- if (!event_dir){
- close (dir_fd);
+ event_dir = fdopendir(events_dir_fd);
+ if (!event_dir)
return -EINVAL;
- }
while ((evt_ent = readdir(event_dir))) {
char *name = evt_ent->d_name;
+ int fd;
FILE *file;
if (!strcmp(name, ".") || !strcmp(name, ".."))
@@ -634,7 +658,7 @@ static int pmu_aliases_parse(struct perf_pmu *pmu)
if (pmu_alias_info_file(name))
continue;
- fd = openat(dir_fd, name, O_RDONLY);
+ fd = openat(events_dir_fd, name, O_RDONLY);
if (fd == -1) {
pr_debug("Cannot open %s\n", name);
continue;
@@ -646,17 +670,57 @@ static int pmu_aliases_parse(struct perf_pmu *pmu)
}
if (perf_pmu__new_alias(pmu, name, /*desc=*/ NULL,
- /*val=*/ NULL, file, /*pe=*/ NULL) < 0)
+ /*val=*/ NULL, file, /*pe=*/ NULL,
+ EVENT_SRC_SYSFS) < 0)
pr_debug("Cannot set up %s\n", name);
fclose(file);
}
closedir(event_dir);
- close (dir_fd);
pmu->sysfs_aliases_loaded = true;
return 0;
}
+static int pmu_aliases_parse(struct perf_pmu *pmu)
+{
+ char path[PATH_MAX];
+ size_t len;
+ int events_dir_fd, ret;
+
+ if (pmu->sysfs_aliases_loaded)
+ return 0;
+
+ len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
+ if (!len)
+ return 0;
+ scnprintf(path + len, sizeof(path) - len, "%s/events", pmu->name);
+
+ events_dir_fd = open(path, O_DIRECTORY);
+ if (events_dir_fd == -1) {
+ pmu->sysfs_aliases_loaded = true;
+ return 0;
+ }
+ ret = __pmu_aliases_parse(pmu, events_dir_fd);
+ close(events_dir_fd);
+ return ret;
+}
+
+static int pmu_aliases_parse_eager(struct perf_pmu *pmu, int sysfs_fd)
+{
+ char path[FILENAME_MAX + 7];
+ int ret, events_dir_fd;
+
+ scnprintf(path, sizeof(path), "%s/events", pmu->name);
+ events_dir_fd = openat(sysfs_fd, path, O_DIRECTORY, 0);
+ if (events_dir_fd == -1) {
+ pmu->sysfs_aliases_loaded = true;
+ return 0;
+ }
+ ret = __pmu_aliases_parse(pmu, events_dir_fd);
+ close(events_dir_fd);
+ return ret;
+}
+
static int pmu_alias_terms(struct perf_pmu_alias *alias, int err_loc, struct list_head *terms)
{
struct parse_events_term *term, *cloned;
@@ -900,7 +964,8 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
{
struct perf_pmu *pmu = vdata;
- perf_pmu__new_alias(pmu, pe->name, pe->desc, pe->event, /*val_fd=*/ NULL, pe);
+ perf_pmu__new_alias(pmu, pe->name, pe->desc, pe->event, /*val_fd=*/ NULL,
+ pe, EVENT_SRC_CPU_JSON);
return 0;
}
@@ -935,13 +1000,14 @@ static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
return 0;
if (pmu_uncore_alias_match(pe->pmu, pmu->name) &&
- pmu_uncore_identifier_match(pe->compat, pmu->id)) {
+ pmu_uncore_identifier_match(pe->compat, pmu->id)) {
perf_pmu__new_alias(pmu,
pe->name,
pe->desc,
pe->event,
/*val_fd=*/ NULL,
- pe);
+ pe,
+ EVENT_SRC_SYS_JSON);
}
return 0;
@@ -993,7 +1059,8 @@ perf_pmu__arch_init(struct perf_pmu *pmu)
pmu->mem_events = perf_mem_events;
}
-struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *name)
+struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *name,
+ bool eager_load)
{
struct perf_pmu *pmu;
__u32 type;
@@ -1022,7 +1089,7 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
* type value and format definitions. Load both right
* now.
*/
- if (pmu_format(pmu, dirfd, name))
+ if (pmu_format(pmu, dirfd, name, eager_load))
goto err;
pmu->is_core = is_pmu_core(name);
@@ -1035,11 +1102,20 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
pmu->max_precise = pmu_max_precise(dirfd, pmu);
pmu->alias_name = pmu_find_alias_name(pmu, dirfd);
pmu->events_table = perf_pmu__find_events_table(pmu);
+ /*
+ * Load the sys json events/aliases when loading the PMU as each event
+ * may have a different compat regular expression. We therefore can't
+ * know the number of sys json events/aliases without computing the
+ * regular expressions for them all.
+ */
pmu_add_sys_aliases(pmu);
list_add_tail(&pmu->list, pmus);
perf_pmu__arch_init(pmu);
+ if (eager_load)
+ pmu_aliases_parse_eager(pmu, dirfd);
+
return pmu;
err:
zfree(&pmu->name);
@@ -1602,6 +1678,62 @@ bool perf_pmu__has_format(const struct perf_pmu *pmu, const char *name)
return false;
}
+int perf_pmu__for_each_format(struct perf_pmu *pmu, void *state, pmu_format_callback cb)
+{
+ static const char *const terms[] = {
+ "config=0..0xffffffffffffffff",
+ "config1=0..0xffffffffffffffff",
+ "config2=0..0xffffffffffffffff",
+ "config3=0..0xffffffffffffffff",
+ "name=string",
+ "period=number",
+ "freq=number",
+ "branch_type=(u|k|hv|any|...)",
+ "time",
+ "call-graph=(fp|dwarf|lbr)",
+ "stack-size=number",
+ "max-stack=number",
+ "nr=number",
+ "inherit",
+ "no-inherit",
+ "overwrite",
+ "no-overwrite",
+ "percore",
+ "aux-output",
+ "aux-sample-size=number",
+ };
+ struct perf_pmu_format *format;
+ int ret;
+
+ /*
+ * max-events and driver-config are missing above as are the internal
+ * types user, metric-id, raw, legacy cache and hardware. Assert against
+ * the enum parse_events__term_type so they are kept in sync.
+ */
+ _Static_assert(ARRAY_SIZE(terms) == __PARSE_EVENTS__TERM_TYPE_NR - 6,
+ "perf_pmu__for_each_format()'s terms must be kept in sync with enum parse_events__term_type");
+ list_for_each_entry(format, &pmu->format, list) {
+ perf_pmu_format__load(pmu, format);
+ ret = cb(state, format->name, (int)format->value, format->bits);
+ if (ret)
+ return ret;
+ }
+ if (!pmu->is_core)
+ return 0;
+
+ for (size_t i = 0; i < ARRAY_SIZE(terms); i++) {
+ int config = PERF_PMU_FORMAT_VALUE_CONFIG;
+
+ if (i < PERF_PMU_FORMAT_VALUE_CONFIG_END)
+ config = i;
+
+ ret = cb(state, terms[i], config, /*bits=*/NULL);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
bool is_pmu_core(const char *name)
{
return !strcmp(name, "cpu") || !strcmp(name, "cpum_cf") || is_sysfs_pmu_core(name);
@@ -1632,15 +1764,15 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu)
{
size_t nr;
- if (!pmu->sysfs_aliases_loaded)
- pmu_aliases_parse(pmu);
-
- nr = pmu->sysfs_aliases;
+ pmu_aliases_parse(pmu);
+ nr = pmu->sysfs_aliases + pmu->sys_json_aliases;;
if (pmu->cpu_aliases_added)
- nr += pmu->loaded_json_aliases;
+ nr += pmu->cpu_json_aliases;
else if (pmu->events_table)
- nr += pmu_events_table__num_events(pmu->events_table, pmu) - pmu->loaded_json_aliases;
+ nr += pmu_events_table__num_events(pmu->events_table, pmu) - pmu->cpu_json_aliases;
+ else
+ assert(pmu->cpu_json_aliases == 0);
return pmu->selectable ? nr + 1 : nr;
}
@@ -1693,11 +1825,16 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
struct strbuf sb;
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;
+ int pmu_name_len;
info.pmu_name = event->pmu_name ?: pmu->name;
+ pmu_name_len = skip_duplicate_pmus
+ ? pmu_name_len_no_suffix(info.pmu_name, /*num=*/NULL)
+ : (int)strlen(info.pmu_name);
info.alias = NULL;
if (event->desc) {
info.name = event->name;
@@ -1722,7 +1859,7 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
info.encoding_desc = buf + buf_used;
parse_events_terms__to_strbuf(&event->terms, &sb);
buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
- "%s/%s/", info.pmu_name, sb.buf) + 1;
+ "%.*s/%s/", pmu_name_len, info.pmu_name, sb.buf) + 1;
info.topic = event->topic;
info.str = sb.buf;
info.deprecated = event->deprecated;
@@ -2003,18 +2140,29 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
name ?: "N/A", buf, config_name, config);
}
-int perf_pmu__match(const char *pattern, const char *name, const char *tok)
+bool perf_pmu__match(const struct perf_pmu *pmu, const char *tok)
{
- if (!name)
- return -1;
+ const char *name = pmu->name;
+ bool need_fnmatch = strchr(tok, '*') != NULL;
- if (fnmatch(pattern, name, 0))
- return -1;
+ if (!strncmp(tok, "uncore_", 7))
+ tok += 7;
+ if (!strncmp(name, "uncore_", 7))
+ name += 7;
- if (tok && !perf_pmu__match_ignoring_suffix(name, tok))
- return -1;
+ if (perf_pmu__match_ignoring_suffix(name, tok) ||
+ (need_fnmatch && !fnmatch(tok, name, 0)))
+ return true;
- return 0;
+ name = pmu->alias_name;
+ if (!name)
+ return false;
+
+ if (!strncmp(name, "uncore_", 7))
+ name += 7;
+
+ return perf_pmu__match_ignoring_suffix(name, tok) ||
+ (need_fnmatch && !fnmatch(tok, name, 0));
}
double __weak perf_pmu__cpu_slots_per_cycle(void)
@@ -2085,3 +2233,22 @@ void perf_pmu__delete(struct perf_pmu *pmu)
zfree(&pmu->id);
free(pmu);
}
+
+const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config)
+{
+ struct perf_pmu_alias *event;
+
+ if (!pmu)
+ return NULL;
+
+ pmu_aliases_parse(pmu);
+ 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);
+
+ if (ret == 0 && config == attr.config)
+ return event->name;
+ }
+ return NULL;
+}