#define UNIT_MAX_LEN 31 /* max length for event unit name */
enum event_source { /* An event loaded from /sys/bus/event_source/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/bus/event_source/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.
*/ struct perf_pmu_alias { /** @name: Name of the event like "mem-loads". */ char *name; /** @desc: Optional short description of the event. */ char *desc; /** @long_desc: Optional long description. */ char *long_desc; /** * @topic: Optional topic such as cache or pipeline, particularly for * json events.
*/ char *topic; /** @terms: Owned list of the original parsed parameters. */ struct parse_events_terms terms; /** * @pmu_name: The name copied from the json struct pmu_event. This can * differ from the PMU name as it won't have suffixes.
*/ char *pmu_name; /** @unit: Units for the event, such as bytes or cache lines. */ char unit[UNIT_MAX_LEN+1]; /** @scale: Value to scale read counter values by. */ double scale; /** @retirement_latency_mean: Value to be given for unsampled retirement latency mean. */ double retirement_latency_mean; /** @retirement_latency_min: Value to be given for unsampled retirement latency min. */ double retirement_latency_min; /** @retirement_latency_max: Value to be given for unsampled retirement latency max. */ double retirement_latency_max; /** * @per_pkg: Does the file * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or * equivalent json value exist and have the value 1.
*/ bool per_pkg; /** * @snapshot: Does the file * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot * exist and have the value 1.
*/ bool snapshot; /** * @deprecated: Is the event hidden and so not shown in perf list by * default.
*/ bool deprecated; /** @from_sysfs: Was the alias from sysfs or a json event? */ bool from_sysfs; /** @info_loaded: Have the scale, unit and other values been read from disk? */ bool info_loaded;
};
/** * struct perf_pmu_format - Values from a format file read from * <sysfs>/devices/cpu/format/ held in struct perf_pmu. * * For example, the contents of <sysfs>/devices/cpu/format/event may be * "config:0-7" and will be represented here as name="event", * value=PERF_PMU_FORMAT_VALUE_CONFIG and bits 0 to 7 will be set.
*/ struct perf_pmu_format { /** @list: Element on list within struct perf_pmu. */ struct list_head list; /** @bits: Which config bits are set by this format value. */
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); /** @name: The modifier/file name. */ char *name; /** * @value : Which config value the format relates to. Supported values * are from PERF_PMU_FORMAT_VALUE_CONFIG to * PERF_PMU_FORMAT_VALUE_CONFIG_END.
*/
u16 value; /** @loaded: Has the contents been loaded/parsed. */ bool loaded;
};
/* Called at the end of parsing a format. */ void perf_pmu_format__set_value(void *vformat, int config, unsignedlong *bits)
{ struct perf_pmu_format *format = vformat;
/* * Parse & process all the sysfs attributes located under * the directory specified in 'dir' parameter.
*/ staticint perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load)
{ struct io_dirent64 *evt_ent; struct io_dir format_dir; int ret = 0;
if (io_dir__is_dir(&format_dir, evt_ent)) continue;
format = perf_pmu__new_format(&pmu->format, name); if (!format) {
ret = -ENOMEM; break;
}
if (eager_load) {
FILE *file; int fd = openat(dirfd, name, O_RDONLY);
if (fd < 0) {
ret = -errno; break;
}
file = fdopen(fd, "r"); if (!file) {
close(fd); break;
}
__perf_pmu_format__load(format, file);
fclose(file);
}
}
close(format_dir.dirfd); return ret;
}
/* * Reading/parsing the default pmu format definition, which should be * located at: * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
*/ staticint pmu_format(struct perf_pmu *pmu, int dirfd, constchar *name, bool eager_load)
{ int fd;
/* it'll close the fd */ if (perf_pmu__format_parse(pmu, fd, eager_load)) return -1;
return 0;
}
staticint parse_double(constchar *scale, char **end, double *sval)
{ char *lc; int ret = 0;
/* * save current locale
*/
lc = setlocale(LC_NUMERIC, NULL);
/* * The lc string may be allocated in static storage, * so get a dynamic copy to make it survive setlocale * call below.
*/
lc = strdup(lc); if (!lc) {
ret = -ENOMEM; goto out;
}
/* * force to C locale to ensure kernel * scale string is converted correctly. * kernel uses default C locale.
*/
setlocale(LC_NUMERIC, "C");
if (hashmap__find(pmu->aliases, name, &alias)) return alias;
if (!load || pmu->sysfs_aliases_loaded) return NULL;
/* * 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); if (hashmap__find(pmu->aliases, name, &alias)) return alias;
}
/* * load unit name and scale if available
*/
perf_pmu__parse_unit(pmu, alias);
perf_pmu__parse_scale(pmu, alias);
perf_pmu__parse_per_pkg(pmu, alias);
perf_pmu__parse_snapshot(pmu, alias);
}
if (!ret && pe && pe->retirement_latency_mean) {
ret = parse_double(pe->retirement_latency_mean, NULL,
&alias->retirement_latency_mean);
} if (!ret && pe && pe->retirement_latency_min) {
ret = parse_double(pe->retirement_latency_min, NULL,
&alias->retirement_latency_min);
} if (!ret && pe && pe->retirement_latency_max) {
ret = parse_double(pe->retirement_latency_max, NULL,
&alias->retirement_latency_max);
} if (ret) return ret;
ret = parse_events_terms(&alias->terms, val, val_fd); if (ret) {
pr_err("Cannot parse alias %s: %d\n", val, ret);
free(alias); return ret;
}
alias->name = strdup(name);
alias->desc = desc ? strdup(desc) : NULL;
alias->long_desc = long_desc ? strdup(long_desc) : NULL;
alias->topic = topic ? strdup(topic) : NULL;
alias->pmu_name = pmu_name ? strdup(pmu_name) : NULL; if (unit) { if (perf_pmu__convert_scale(unit, (char **)&unit, &alias->scale) < 0) {
perf_pmu_free_alias(alias); return -1;
}
snprintf(alias->unit, sizeof(alias->unit), "%s", unit);
} 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->cpu_common_json_aliases++;
}
pmu->sysfs_aliases++; break; case EVENT_SRC_CPU_JSON:
pmu->cpu_json_aliases++; break; case EVENT_SRC_SYS_JSON:
pmu->sys_json_aliases++; break;
len = strlen(name); if (len > 5 && !strcmp(name + len - 5, ".unit")) returntrue; if (len > 6 && !strcmp(name + len - 6, ".scale")) returntrue; if (len > 8 && !strcmp(name + len - 8, ".per-pkg")) returntrue; if (len > 9 && !strcmp(name + len - 9, ".snapshot")) returntrue;
returnfalse;
}
/* * Reading the pmu event aliases definition, which should be located at: * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
*/ staticint __pmu_aliases_parse(struct perf_pmu *pmu, int events_dir_fd)
{ struct io_dirent64 *evt_ent; struct io_dir event_dir;
io_dir__init(&event_dir, events_dir_fd);
while ((evt_ent = io_dir__readdir(&event_dir))) { char *name = evt_ent->d_name; int fd;
FILE *file;
if (!strcmp(name, ".") || !strcmp(name, "..")) continue;
/* * skip info files parsed in perf_pmu__new_alias()
*/ if (pmu_alias_info_file(name)) continue;
fd = openat(events_dir_fd, name, O_RDONLY); if (fd == -1) {
pr_debug("Cannot open %s\n", name); continue;
}
file = fdopen(fd, "r"); if (!file) {
close(fd); continue;
}
if (perf_pmu__new_alias(pmu, name, /*desc=*/ NULL, /*val=*/ NULL, file, /*pe=*/ NULL,
EVENT_SRC_SYSFS) < 0)
pr_debug("Cannot set up %s\n", name);
fclose(file);
}
if (filename__read_str(path, &str, &len) < 0) return NULL;
str[len - 1] = 0; /* remove line feed */
return str;
}
/** * is_sysfs_pmu_core() - PMU CORE devices have different name other than cpu in * sysfs on some platforms like ARM or Intel hybrid. Looking for * possible the cpus file in sysfs files to identify whether this is a * core device. * @name: The PMU name such as "cpu_atom".
*/ staticint is_sysfs_pmu_core(constchar *name)
{ char path[PATH_MAX];
if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "cpus")) return 0; return file_available(path);
}
/** * Return the length of the PMU name not including the suffix for uncore PMUs. * * We want to deduplicate many similar uncore PMUs by stripping their suffixes, * but there are never going to be too many core PMUs and the suffixes might be * interesting. "arm_cortex_a53" vs "arm_cortex_a57" or "cpum_cf" for example. * * @skip_duplicate_pmus: False in verbose mode so all uncore PMUs are visible
*/ static size_t pmu_deduped_name_len(conststruct perf_pmu *pmu, constchar *name, bool skip_duplicate_pmus)
{ return skip_duplicate_pmus && !pmu->is_core
? pmu_name_len_no_suffix(name)
: strlen(name);
}
/** * perf_pmu__match_wildcard - Does the pmu_name start with tok and is then only * followed by nothing or a suffix? tok may contain * part of a suffix. * @pmu_name: The pmu_name with possible suffix. * @tok: The wildcard argument to match.
*/ staticbool perf_pmu__match_wildcard(constchar *pmu_name, constchar *tok)
{ constchar *p, *suffix; bool has_hex = false;
size_t tok_len = strlen(tok);
/* Check start of pmu_name for equality. */ if (strncmp(pmu_name, tok, tok_len)) returnfalse;
suffix = p = pmu_name + tok_len; if (*p == 0) returntrue;
if (*p == '_') {
++p;
++suffix;
}
/* Ensure we end in a number */ while (1) { if (!isxdigit(*p)) returnfalse; if (!has_hex)
has_hex = !isdigit(*p); if (*(++p) == 0) break;
}
if (has_hex) return (p - suffix) > 2;
returntrue;
}
/** * perf_pmu__match_ignoring_suffix_uncore - Does the pmu_name match tok ignoring * any trailing suffix on pmu_name and * tok? The Suffix must be in form * tok_{digits}, or tok{digits}. * @pmu_name: The pmu_name with possible suffix. * @tok: The possible match to pmu_name.
*/ staticbool perf_pmu__match_ignoring_suffix_uncore(constchar *pmu_name, constchar *tok)
{
size_t pmu_name_len, tok_len;
/* For robustness, check for NULL. */ if (pmu_name == NULL) return tok == NULL;
/* uncore_ prefixes are ignored. */ if (!strncmp(pmu_name, "uncore_", 7))
pmu_name += 7; if (!strncmp(tok, "uncore_", 7))
tok += 7;
/** * perf_pmu__match_wildcard_uncore - does to_match match the PMU's name? * @pmu_name: The pmu->name or pmu->alias to match against. * @to_match: the json struct pmu_event name. This may lack a suffix (which * matches) or be of the form "socket,pmuname" which will match * "socketX_pmunameY".
*/ staticbool perf_pmu__match_wildcard_uncore(constchar *pmu_name, constchar *to_match)
{ char *mutable_to_match, *tok, *tmp;
if (!pmu_name) returnfalse;
/* uncore_ prefixes are ignored. */ if (!strncmp(pmu_name, "uncore_", 7))
pmu_name += 7; if (!strncmp(to_match, "uncore_", 7))
to_match += 7;
if (strchr(to_match, ',') == NULL) return perf_pmu__match_wildcard(pmu_name, to_match);
/* Process comma separated list of PMU name components. */
mutable_to_match = strdup(to_match); if (!mutable_to_match) returnfalse;
tok = strtok_r(mutable_to_match, ",", &tmp); while (tok) {
size_t tok_len = strlen(tok);
if (strncmp(pmu_name, tok, tok_len)) { /* Mismatch between part of pmu_name and tok. */
free(mutable_to_match); returnfalse;
} /* Move pmu_name forward over tok and suffix. */
pmu_name += tok_len; while (*pmu_name != '\0' && isdigit(*pmu_name))
pmu_name++; if (*pmu_name == '_')
pmu_name++;
/* * From the pmu_events_table, find the events that correspond to the given * PMU and add them to the list 'head'.
*/ void pmu_add_cpu_aliases_table(struct perf_pmu *pmu, conststruct pmu_events_table *table)
{
pmu_events_table__for_each_event(table, pmu, pmu_add_cpu_aliases_map_callback, pmu);
}
staticvoid pmu_add_cpu_aliases(struct perf_pmu *pmu)
{ if (!pmu->events_table) return;
/* Variant of str_hash that does tolower on each character. */ static size_t aliases__hash(long key, void *ctx __maybe_unused)
{ constchar *s = (constchar *)key;
size_t h = 0;
while (*s) {
h = h * 31 + tolower(*s);
s++;
} return h;
}
/* * Read type early to fail fast if a lookup name isn't a PMU. Ensure * that type value is successfully assigned (return 1).
*/ if (perf_pmu__scan_file_at(pmu, dirfd, "type", "%u", &pmu->type) != 1) { /* Double check the PMU's name isn't wellknown. */
pmu->type = wellknown_pmu_type(name); if (pmu->type == PERF_TYPE_MAX) {
perf_pmu__delete(pmu); return NULL;
}
}
/* * The pmu data we store & need consists of the pmu * type value and format definitions. Load both right * now.
*/ if (pmu_format(pmu, dirfd, name, eager_load)) {
perf_pmu__delete(pmu); return NULL;
}
pmu->is_uncore = pmu_is_uncore(dirfd, name); if (pmu->is_uncore)
pmu->id = pmu_id(name);
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;
}
/* Creates the PMU when sysfs scanning fails. */ struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus)
{ struct perf_pmu *pmu = zalloc(sizeof(*pmu));
if (!pmu) return NULL;
pmu->name = strdup("cpu"); if (!pmu->name) {
free(pmu); return NULL;
}
/* fake pmu doesn't have format list */ if (perf_pmu__is_fake(pmu)) return;
list_for_each_entry(format, &pmu->format, list) {
perf_pmu_format__load(pmu, format); if (format->value >= PERF_PMU_FORMAT_VALUE_CONFIG_END) {
pr_warning("WARNING: '%s' format '%s' requires 'perf_event_attr::config%d'" "which is not supported by this version of perf!\n",
pmu->name, format->name, format->value); return;
}
}
}
/* * Set @config_name to @val as long as the user hasn't already set or cleared it * by passing a config term on the command line. * * @val is the value to put into the bits specified by @config_name rather than * the bit pattern. It is shifted into position by this function, so to set * something to true, pass 1 for val rather than a pre shifted value.
*/ #define field_prep(_mask, _val) (((_val) << (ffsll(_mask) - 1)) & (_mask)) void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel, constchar *config_name, u64 val)
{
u64 user_bits = 0, bits; struct evsel_config_term *term = evsel__get_config_term(evsel, CFG_CHG);
if (term)
user_bits = term->val.cfg_chg;
bits = perf_pmu__format_bits(pmu, config_name);
/* Do nothing if the user changed the value */ if (bits & user_bits) return;
/* * Sets value based on the format definition (format parameter) * and unformatted value (value parameter).
*/ staticvoid pmu_format_value(unsignedlong *format, __u64 value, __u64 *v, bool zero)
{ unsignedlong fbit, vbit;
static __u64 pmu_format_max_value(constunsignedlong *format)
{ int w;
w = bitmap_weight(format, PERF_PMU_FORMAT_BITS); if (!w) return 0; if (w < 64) return (1ULL << w) - 1; return -1;
}
/* * Term is a string term, and might be a param-term. Try to look up it's value * in the remaining terms. * - We have a term like "base-or-format-term=param-term", * - We need to find the value supplied for "param-term" (with param-term named * in a config string) later on in the term list.
*/ staticint pmu_resolve_param_term(struct parse_events_term *term, struct parse_events_terms *head_terms,
__u64 *value)
{ struct parse_events_term *t;
/* * Setup one of config[12] attr members based on the * user input data - term parameter.
*/ staticint pmu_config_term(conststruct perf_pmu *pmu, struct perf_event_attr *attr, struct parse_events_term *term, struct parse_events_terms *head_terms, bool zero, bool apply_hardcoded, struct parse_events_error *err)
{ struct perf_pmu_format *format;
__u64 *vp;
__u64 val, max_val;
/* * If this is a parameter we've already used for parameterized-eval, * skip it in normal eval.
*/ if (term->used) return 0;
/* * 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)) { /* 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_CPU: /* Skip non-config terms. */ break; default: break;
} return 0;
}
format = pmu_find_format(&pmu->format, term->config); if (!format) { char *pmu_term = pmu_formats_string(&pmu->format); char *unknown_term; char *help_msg;
if (asprintf(&unknown_term, "unknown term '%s' for pmu '%s'",
term->config, pmu->name) < 0)
unknown_term = NULL;
help_msg = parse_events_formats_error_string(pmu_term); if (err) {
parse_events_error__handle(err, term->err_term,
unknown_term,
help_msg);
} else {
pr_debug("%s (%s)\n", unknown_term, help_msg);
free(unknown_term);
}
free(pmu_term); return -EINVAL;
}
perf_pmu_format__load(pmu, format); switch (format->value) { case PERF_PMU_FORMAT_VALUE_CONFIG:
vp = &attr->config; break; case PERF_PMU_FORMAT_VALUE_CONFIG1:
vp = &attr->config1; break; case PERF_PMU_FORMAT_VALUE_CONFIG2:
vp = &attr->config2; break; case PERF_PMU_FORMAT_VALUE_CONFIG3:
vp = &attr->config3; break; default: return -EINVAL;
}
/* * Either directly use a numeric term, or try to translate string terms * using event parameters.
*/ if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { if (term->no_value &&
bitmap_weight(format->bits, PERF_PMU_FORMAT_BITS) > 1) { if (err) {
parse_events_error__handle(err, term->err_val,
strdup("no value assigned for term"),
NULL);
} return -EINVAL;
}
val = term->val.num;
} elseif (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { if (strcmp(term->val.str, "?")) { if (verbose > 0) {
pr_info("Invalid sysfs entry %s=%s\n",
term->config, term->val.str);
} if (err) {
parse_events_error__handle(err, term->err_val,
strdup("expected numeric value"),
NULL);
} return -EINVAL;
}
if (pmu_resolve_param_term(term, head_terms, &val)) return -EINVAL;
} else return -EINVAL;
max_val = pmu_format_max_value(format->bits); if (val > max_val) { if (err) { char *err_str;
if (asprintf(&err_str, "value too big for format (%s), maximum is %llu",
format->name, (unsignedlonglong)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;
} /* * Assume we don't care if !err, in which case the value will be * silently truncated.
*/
}
if (parse_events__is_hardcoded_term(term)) return NULL;
if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { if (!term->no_value) return NULL; if (pmu_find_format(&pmu->format, term->config)) return NULL;
name = term->config;
} elseif (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { if (strcasecmp(term->config, "event")) return NULL;
name = term->val.str;
} else { return NULL;
}
alias = perf_pmu__find_alias(pmu, name, /*load=*/ true); if (alias || pmu->cpu_aliases_added) return alias;
/* Alias doesn't exist, try to get it from the json events. */ if (pmu->events_table &&
pmu_events_table__find_event(pmu->events_table, pmu, name,
pmu_add_cpu_aliases_map_callback,
pmu) == 0) {
alias = perf_pmu__find_alias(pmu, name, /*load=*/ false);
} return alias;
}
staticint check_info_data(struct perf_pmu *pmu, struct perf_pmu_alias *alias, struct perf_pmu_info *info, struct parse_events_error *err, int column)
{
read_alias_info(pmu, alias); /* * Only one term in event definition can * define unit, scale and snapshot, fail * if there's more than one.
*/ if (info->unit && alias->unit[0]) {
parse_events_error__handle(err, column,
strdup("Attempt to set event's unit twice"),
NULL); return -EINVAL;
} if (info->scale && alias->scale) {
parse_events_error__handle(err, column,
strdup("Attempt to set event's scale twice"),
NULL); return -EINVAL;
} if (info->snapshot && alias->snapshot) {
parse_events_error__handle(err, column,
strdup("Attempt to set event snapshot twice"),
NULL); return -EINVAL;
}
if (alias->unit[0])
info->unit = alias->unit;
if (alias->scale)
info->scale = alias->scale;
if (alias->snapshot)
info->snapshot = alias->snapshot;
return 0;
}
/* * Find alias in the terms list and replace it with the terms * defined for the alias
*/ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_terms, struct perf_pmu_info *info, bool *rewrote_terms,
u64 *alternate_hw_config, struct parse_events_error *err)
{ struct parse_events_term *term, *h; struct perf_pmu_alias *alias; int ret;
*rewrote_terms = false;
info->per_pkg = false;
/* * Mark unit and scale as not set * (different from default values, see below)
*/
info->unit = NULL;
info->scale = 0.0;
info->snapshot = false;
info->retirement_latency_mean = 0.0;
info->retirement_latency_min = 0.0;
info->retirement_latency_max = 0.0;
if (perf_pmu__is_hwmon(pmu)) {
ret = hwmon_pmu__check_alias(head_terms, info, err); goto out;
} if (perf_pmu__is_drm(pmu)) {
ret = drm_pmu__check_alias(pmu, head_terms, info, err); goto out;
}
list_del_init(&term->list);
parse_events_term__delete(term);
}
out: /* * if no unit or scale found in aliases, then * set defaults as for evsel * unit cannot left to NULL
*/ if (info->unit == NULL)
info->unit = "";
/* Sub-optimal, but function is only used by tests. */ return perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ false,
&args, find_event_callback);
}
/* * 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;
}
if (perf_pmu__is_tracepoint(pmu)) return tp_pmu__num_events(pmu); if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__num_events(pmu); if (perf_pmu__is_drm(pmu)) return drm_pmu__num_events(pmu);
pmu_aliases_parse(pmu);
nr = pmu->sysfs_aliases + pmu->sys_json_aliases;
if (pmu->cpu_aliases_added)
nr += pmu->cpu_json_aliases; elseif (pmu->events_table)
nr += pmu_events_table__num_events(pmu->events_table, pmu) -
pmu->cpu_common_json_aliases; else
assert(pmu->cpu_json_aliases == 0 && pmu->cpu_common_json_aliases == 0);
if (perf_pmu__is_tool(pmu))
nr -= tool_pmu__num_skip_events();
return pmu->selectable ? nr + 1 : nr;
}
staticint sub_non_neg(int a, int b)
{ if (b > a) return 0; return a - b;
}
staticbool perf_pmu___name_match(conststruct perf_pmu *pmu, constchar *to_match, bool wildcard)
{ constchar *names[2] = {
pmu->name,
pmu->alias_name,
}; if (pmu->is_core) { for (size_t i = 0; i < ARRAY_SIZE(names); i++) { constchar *name = names[i];
if (!name) continue;
if (!strcmp(name, to_match)) { /* Exact name match. */ returntrue;
}
} if (!strcmp(to_match, "default_core")) { /* * jevents and tests use default_core as a marker for any core * PMU as the PMU name varies across architectures.
*/ returntrue;
} returnfalse;
} if (!pmu->is_uncore) { /* * PMU isn't core or uncore, some kind of broken CPU mask * situation. Only match exact name.
*/ for (size_t i = 0; i < ARRAY_SIZE(names); i++) { constchar *name = names[i];
if (!name) continue;
if (!strcmp(name, to_match)) { /* Exact name match. */ returntrue;
}
} returnfalse;
} for (size_t i = 0; i < ARRAY_SIZE(names); i++) { constchar *name = names[i];
if (!name) continue;
if (wildcard && perf_pmu__match_wildcard_uncore(name, to_match)) returntrue; if (!wildcard && perf_pmu__match_ignoring_suffix_uncore(name, to_match)) returntrue;
} returnfalse;
}
/** * perf_pmu__name_wildcard_match - Called by the jevents generated code to see * if pmu matches the json to_match string. * @pmu: The pmu whose name/alias to match. * @to_match: The possible match to pmu_name.
*/ bool perf_pmu__name_wildcard_match(conststruct perf_pmu *pmu, constchar *to_match)
{ return perf_pmu___name_match(pmu, to_match, /*wildcard=*/true);
}
/** * perf_pmu__name_no_suffix_match - Does pmu's name match to_match ignoring any * trailing suffix on the pmu_name and/or tok? * @pmu: The pmu whose name/alias to match. * @to_match: The possible match to pmu_name.
*/ bool perf_pmu__name_no_suffix_match(conststruct perf_pmu *pmu, constchar *to_match)
{ return perf_pmu___name_match(pmu, to_match, /*wildcard=*/false);
}
if (pmu->is_core || pmu->is_uncore || pmu->auxtrace) returnfalse; switch (pmu->type) { case PERF_TYPE_HARDWARE: returnfalse; case PERF_TYPE_SOFTWARE: returntrue; case PERF_TYPE_TRACEPOINT: returntrue; case PERF_TYPE_HW_CACHE: returnfalse; case PERF_TYPE_RAW: returnfalse; case PERF_TYPE_BREAKPOINT: returntrue; case PERF_PMU_TYPE_TOOL: returntrue; default: break;
} for (size_t i = 0; i < ARRAY_SIZE(known_sw_pmus); i++) { if (!strcmp(pmu->name, known_sw_pmus[i])) returntrue;
} returnfalse;
}
/* * Reading/parsing the given pmu capabilities, which should be located at: * /sys/bus/event_source/devices/<dev>/caps as sysfs group attributes. * Return the number of capabilities
*/ int perf_pmu__caps_parse(struct perf_pmu *pmu)
{ char caps_path[PATH_MAX]; struct io_dir caps_dir; struct io_dirent64 *evt_ent; int caps_fd;
if (pmu->caps_initialized) return pmu->nr_caps;
pmu->nr_caps = 0;
if (!perf_pmu__pathname_scnprintf(caps_path, sizeof(caps_path), pmu->name, "caps")) return -1;
caps_fd = open(caps_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY); if (caps_fd == -1) {
pmu->caps_initialized = true; return 0; /* no error if caps does not exist */
}
io_dir__init(&caps_dir, caps_fd);
while ((evt_ent = io_dir__readdir(&caps_dir)) != NULL) { char *name = evt_ent->d_name; char value[128];
FILE *file; int fd;
if (io_dir__is_dir(&caps_dir, evt_ent)) continue;
fd = openat(caps_fd, name, O_RDONLY); if (fd == -1) continue;
file = fdopen(fd, "r"); if (!file) {
close(fd); continue;
}
/* * Fill 'buf' with the path to a file or folder in 'pmu_name' in * sysfs. For example if pmu_name = "cs_etm" and 'filename' = "format" * then pathname will be filled with * "/sys/bus/event_source/devices/cs_etm/format" * * Return 0 if the sysfs mountpoint couldn't be found, if no characters were * written or if the buffer size is exceeded.
*/ int perf_pmu__pathname_scnprintf(char *buf, size_t size, constchar *pmu_name, constchar *filename)
{
size_t len;
len = perf_pmu__event_source_devices_scnprintf(buf, size); if (!len || (len + strlen(pmu_name) + strlen(filename) + 1) >= size) return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.