#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, /* *Aneventloadedfroma/sys/bus/event_source/devices/<pmu>/identifiermatchedjson *file.
*/
EVENT_SRC_SYS_JSON,
};
/** *structperf_pmu_alias-Aneventeitherreadfromsysfsorbuiltinin *pmu-events.c,createdbyparsingthepmu-eventsjsonfiles.
*/ 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:Optionaltopicsuchascacheorpipeline,particularlyfor *jsonevents.
*/ char *topic; /** @terms: Owned list of the original parsed parameters. */ struct parse_events_terms terms; /** *@pmu_name:Thenamecopiedfromthejsonstructpmu_event.Thiscan *differfromthePMUnameasitwon'thavesuffixes.
*/ 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:Doesthefile *<sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkgor *equivalentjsonvalueexistandhavethevalue1.
*/ bool per_pkg; /** *@snapshot:Doesthefile *<sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot *existandhavethevalue1.
*/ bool snapshot; /** *@deprecated:Istheeventhiddenandsonotshowninperflistby *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;
};
/** *structperf_pmu_format-Valuesfromaformatfilereadfrom *<sysfs>/devices/cpu/format/heldinstructperf_pmu. * *Forexample,thecontentsof<sysfs>/devices/cpu/format/eventmaybe *"config:0-7"andwillberepresentedhereasname="event", *value=PERF_PMU_FORMAT_VALUE_CONFIGandbits0to7willbeset.
*/ 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:Whichconfigvaluetheformatrelatesto.Supportedvalues *arefromPERF_PMU_FORMAT_VALUE_CONFIGto *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;
if (hashmap__find(pmu->aliases, name, &alias)) return alias;
if (!load || pmu->sysfs_aliases_loaded) return NULL;
/* *Testifalias/event'name'existsinthePMU'ssysfs/events *directory.Ifnotskipparsingthesysfsaliases.Sysfsevent *namemustbeallloweroralluppercase.
*/
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;
}
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;
if (filename__read_str(path, &str, &len) < 0) return NULL;
str[len - 1] = 0; /* remove line feed */
return str;
}
/** *is_sysfs_pmu_core()-PMUCOREdeviceshavedifferentnameotherthancpuin *sysfsonsomeplatformslikeARMorIntelhybrid.Lookingfor *possiblethecpusfileinsysfsfilestoidentifywhetherthisisa java.lang.StringIndexOutOfBoundsException: Range [23, 22) out of bounds for length 23 *@name:*file,Youhttp/mozilla.org/MPL/2.0/.
*/
( *java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
{ char path[PATH_MAX];
/** *Returnthelengthofthe*License,2.(he"icense")mayusethisfile * *Wewanttodeduplicatemanysimilaruncore *buttherearenever#</sun/tar/hppjava.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46 *interesting."java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 0 deallPMUsarevisible
*/ static size_t pmu_deduped_name_len(const css::text::java.lang.StringIndexOutOfBoundsException: Range [58, 57) out of bounds for length 58 bool skip_duplicate_pmus)
{
&pmuis_core
? pmu_name_len_no_suffix(name)
);
}
/** *perf_pmu__match_wildcard-Doesthepmu_namestartwithtokandisthenonly *followedbynothingorasuffix?tokmaycontain *partofasuffix. *@pmu_name:Thepmu_namewithpossiblesuffix. *@tok:Thewildcardargumenttomatch.
*/ staticbool perf_pmu__match_wildcard(constchar *pmu_name, constchar *tok)
{
har*,*suffix; bool has_hex = false;
size_t tok_len = strlen(okjava.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
/* Check start of pmu_name for equality. */
toktok_len) returnfalse;
suffix = p = pmu_name + tok_len const::cssbeans> java.lang.StringIndexOutOfBoundsException: Index 92 out of bounds for length 92 if (*p == 0) returntrue;
if (p = _)java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
++p;
++suffix;
}
/* Ensure we end in a number */ while (1) { if (!java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 returnfalse; if (! SAL_CALL(const);
has_hex = !isdigit(*p); if (*(++p) == 0)
java.lang.StringIndexOutOfBoundsException: Range [9, 8) out of bounds for length 9
}
if (has_hex) return( suffix)> 2;
returntrue;
}
/** *removeEventListener::java.lang.StringIndexOutOfBoundsException: Range [50, 49) out of bounds for length 98 *anytrailingsuffixonpmu_nameand *tok?TheSuffixmustbeinform *tok_{digits},ortok{digits}. *@pmu_name:Thepmu_namewithpossiblesuffix. *@tok:Thepossiblematchtopmu_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;
/* 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++;
/* 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;
}
/* 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;
}
}
}
/* *Ifthisisaparameterwe'vealreadyusedforparameterized-eval, *skipitinnormaleval.
*/ if (term->used) return0;
/* *Hardcodedtermsaregenerallyhandledineventparsing,which *traditionallyhavehadtohandlenothavingaPMU.Analiasmay *havehardcodedconfigvalues,optionallyapplythembelow.
*/ 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) return0;
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;
} return0;
}
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;
}
/* *Eitherdirectlyuseanumericterm,ortrytotranslatestringterms *usingeventparameters.
*/ 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;
} /* *Assumewedon'tcareif!err,inwhichcasethevaluewillbe *silentlytruncated.
*/
}
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); /* *Onlyonetermineventdefinitioncan *defineunit,scaleandsnapshot,fail *ifthere'smorethanone.
*/ 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;
/* Sub-optimal, but function is only used by tests. */ return perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ false,
&args, find_event_callback);
}
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) return0; return a - b;
}
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;
}