/** * The metric under construction. The data held here will be placed in a * metric_expr.
*/ struct metric { struct list_head nd; /** * The expression parse context importantly holding the IDs contained * within the expression.
*/ struct expr_parse_ctx *pctx; constchar *pmu; /** The name of the metric such as "IPC". */ constchar *metric_name; /** Modifier on the metric such as "u" or NULL for none. */ constchar *modifier; /** The expression to parse, for example, "instructions/cycles". */ constchar *metric_expr; /** Optional threshold expression where zero value is green, otherwise red. */ constchar *metric_threshold; /** * The "ScaleUnit" that scales and adds a unit to the metric during * output.
*/ constchar *metric_unit; /** * Optional name of the metric group reported * if the Default metric group is being processed.
*/ constchar *default_metricgroup_name; /** Optional null terminated array of referenced metrics. */ struct metric_ref *metric_refs; /** * Should events of the metric be grouped?
*/ bool group_events; /** * Parsed events for the metric. Optional as events may be taken from a * different metric whose group contains all the IDs necessary for this * one.
*/ struct evlist *evlist;
};
return m;
out_err:
metric__free(m); return NULL;
}
staticbool contains_metric_id(struct evsel **metric_events, int num_events, constchar *metric_id)
{ int i;
for (i = 0; i < num_events; i++) { if (!strcmp(evsel__metric_id(metric_events[i]), metric_id)) returntrue;
} returnfalse;
}
/** * setup_metric_events - Find a group of events in metric_evlist that correspond * to the IDs from a parsed metric expression. * @pmu: The PMU for the IDs. * @ids: the metric IDs to match. * @metric_evlist: the list of perf events. * @out_metric_events: holds the created metric events array.
*/ staticint setup_metric_events(constchar *pmu, struct hashmap *ids, struct evlist *metric_evlist, struct evsel ***out_metric_events)
{ struct evsel **metric_events; constchar *metric_id; struct evsel *ev;
size_t ids_size, matched_events, i; bool all_pmus = !strcmp(pmu, "all") || perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu);
/* Don't match events for the wrong hybrid PMU. */ if (!all_pmus && ev->pmu && evsel__is_hybrid(ev) &&
strcmp(ev->pmu->name, pmu)) continue; /* * Check for duplicate events with the same name. For * example, uncore_imc/cas_count_read/ will turn into 6 * events per socket on skylakex. Only the first such * event is placed in metric_events.
*/
metric_id = evsel__metric_id(ev); if (contains_metric_id(metric_events, matched_events, metric_id)) continue; /* * Does this event belong to the parse context? For * combined or shared groups, this metric may not care * about this event.
*/ if (hashmap__find(ids, metric_id, &val_ptr)) {
pr_debug("Matched metric-id %s to %s\n", metric_id, evsel__name(ev));
metric_events[matched_events++] = ev;
if (matched_events >= ids_size) break;
}
} if (matched_events < ids_size) {
free(metric_events); return -EINVAL;
} for (i = 0; i < ids_size; i++) {
ev = metric_events[i];
ev->collect_stat = true;
/* * The metric leader points to the identically named * event in metric_events.
*/
ev->metric_leader = ev; /* * Mark two events with identical names in the same * group (or globally) as being in use as uncore events * may be duplicated for each pmu. Set the metric leader * of such events to be the event that appears in * metric_events.
*/
metric_id = evsel__metric_id(ev);
evlist__for_each_entry_continue(metric_evlist, ev) { if (!strcmp(evsel__metric_id(ev), metric_id))
ev->metric_leader = metric_events[i];
}
}
*out_metric_events = metric_events; return 0;
}
staticbool match_metric_or_groups(constchar *metric_or_groups, constchar *sought)
{ int len; char *m;
if (!sought) returnfalse; if (!strcmp(sought, "all")) returntrue; if (!metric_or_groups) return !strcasecmp(sought, "No_group");
len = strlen(sought); if (!strncasecmp(metric_or_groups, sought, len) &&
(metric_or_groups[len] == 0 || metric_or_groups[len] == ';')) returntrue;
m = strchr(metric_or_groups, ';'); return m && match_metric_or_groups(m + 1, sought);
}
evlist__for_each_entry(perf_evlist, ev) { if (!ev->metric_id) continue;
ret = strbuf_setlen(&sb, 0); if (ret) break;
ret = decode_metric_id(&sb, ev->metric_id); if (ret) break;
free((char *)ev->metric_id);
ev->metric_id = strdup(sb.buf); if (!ev->metric_id) {
ret = -ENOMEM; break;
} /* * If the name is just the parsed event, use the metric-id to * give a more friendly display version.
*/ if (strstr(ev->name, "metric-id=")) { bool has_slash = false;
zfree(&ev->name); for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) {
*cur = '/';
has_slash = true;
}
if (modifier) { if (!has_slash && !strchr(sb.buf, ':')) {
ret = strbuf_addch(&sb, ':'); if (ret) break;
}
ret = strbuf_addstr(&sb, modifier); if (ret) break;
}
ev->name = strdup(sb.buf); if (!ev->name) {
ret = -ENOMEM; break;
}
}
}
strbuf_release(&sb); return ret;
}
/* Always move tool events outside of the group. */
ev = tool_pmu__str_to_event(id); if (ev != TOOL_PMU__EVENT_NONE) {
has_tool_events = true;
tool_events[ev] = true; continue;
} /* Separate events with commas and open the group if necessary. */ if (no_group) { if (group_events) {
ret = strbuf_addch(events, '{');
RETURN_IF_NON_ZERO(ret);
}
no_group = false;
} else {
ret = strbuf_addch(events, ',');
RETURN_IF_NON_ZERO(ret);
} /* * Encode the ID as an event string. Add a qualifier for * metric_id that is the original name except with characters * that parse-events can't parse replaced. For example, * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/
*/
sep = strchr(id, '@'); if (sep != NULL) {
ret = strbuf_add(events, id, sep - id);
RETURN_IF_NON_ZERO(ret);
ret = strbuf_addch(events, '/');
RETURN_IF_NON_ZERO(ret);
rsep = strrchr(sep, '@');
ret = strbuf_add(events, sep + 1, rsep - sep - 1);
RETURN_IF_NON_ZERO(ret);
ret = strbuf_addstr(events, ",metric-id=");
RETURN_IF_NON_ZERO(ret);
sep = rsep;
} else {
sep = strchr(id, ':'); if (sep != NULL) {
ret = strbuf_add(events, id, sep - id);
RETURN_IF_NON_ZERO(ret);
} else {
ret = strbuf_addstr(events, id);
RETURN_IF_NON_ZERO(ret);
}
ret = strbuf_addstr(events, "/metric-id=");
RETURN_IF_NON_ZERO(ret);
}
ret = encode_metric_id(events, id);
RETURN_IF_NON_ZERO(ret);
ret = strbuf_addstr(events, "/");
RETURN_IF_NON_ZERO(ret);
if (sep != NULL) {
ret = strbuf_addstr(events, sep + 1);
RETURN_IF_NON_ZERO(ret);
} if (modifier) {
ret = strbuf_addstr(events, modifier);
RETURN_IF_NON_ZERO(ret);
}
} if (!no_group && group_events) {
ret = strbuf_addf(events, "}:W");
RETURN_IF_NON_ZERO(ret);
} if (has_tool_events) { int 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, tool_pmu__event_to_str(i));
RETURN_IF_NON_ZERO(ret);
}
}
}
return ret; #undef RETURN_IF_NON_ZERO
}
int __weak arch_get_runtimeparam(conststruct pmu_metric *pm __maybe_unused)
{ return 1;
}
/* * A singly linked list on the stack of the names of metrics being * processed. Used to identify recursion.
*/ struct visited_metric { constchar *name; conststruct visited_metric *parent;
};
/** * resolve_metric - Locate metrics within the root metric and recursively add * references to them. * @metric_list: The list the metric is added to. * @pmu: The PMU name to resolve metrics on, or "all" for all PMUs. * @modifier: if non-null event modifiers like "u". * @metric_no_group: Should events written to events be grouped "{}" or * global. Grouping is the default but due to multiplexing the * user may override. * @user_requested_cpu_list: Command line specified CPUs to record on. * @system_wide: Are events for all processes recorded. * @root_metric: Metrics may reference other metrics to form a tree. In this * case the root_metric holds all the IDs and a list of referenced * metrics. When adding a root this argument is NULL. * @visited: A singly linked list of metric names being added that is used to * detect recursion. * @table: The table that is searched for metrics, most commonly the table for the * architecture perf is running upon.
*/ staticint resolve_metric(struct list_head *metric_list, struct perf_pmu *pmu, constchar *modifier, bool metric_no_group, bool metric_no_threshold, constchar *user_requested_cpu_list, bool system_wide, struct metric *root_metric, conststruct visited_metric *visited, conststruct pmu_metrics_table *table)
{ struct hashmap_entry *cur;
size_t bkt; struct to_resolve { /* The metric to resolve. */ struct pmu_metric pm; /* * The key in the IDs map, this may differ from in case, * etc. from pm->metric_name.
*/ constchar *key;
} *pending = NULL; int i, ret = 0, pending_cnt = 0;
/* * Iterate all the parsed IDs and if there's a matching metric and it to * the pending array.
*/
hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) { struct pmu_metric pm;
/* Remove the metric IDs from the context. */ for (i = 0; i < pending_cnt; i++)
expr__del_id(root_metric->pctx, pending[i].key);
/* * Recursively add all the metrics, IDs are added to the root metric's * context.
*/ for (i = 0; i < pending_cnt; i++) {
ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group,
metric_no_threshold, user_requested_cpu_list, system_wide,
root_metric, visited, table); if (ret) break;
}
free(pending); return ret;
}
/** * __add_metric - Add a metric to metric_list. * @metric_list: The list the metric is added to. * @pm: The pmu_metric containing the metric to be added. * @modifier: if non-null event modifiers like "u". * @metric_no_group: Should events written to events be grouped "{}" or * global. Grouping is the default but due to multiplexing the * user may override. * @metric_no_threshold: Should threshold expressions be ignored? * @runtime: A special argument for the parser only known at runtime. * @user_requested_cpu_list: Command line specified CPUs to record on. * @system_wide: Are events for all processes recorded. * @root_metric: Metrics may reference other metrics to form a tree. In this * case the root_metric holds all the IDs and a list of referenced * metrics. When adding a root this argument is NULL. * @visited: A singly linked list of metric names being added that is used to * detect recursion. * @table: The table that is searched for metrics, most commonly the table for the * architecture perf is running upon.
*/ staticint __add_metric(struct list_head *metric_list, conststruct pmu_metric *pm, constchar *modifier, bool metric_no_group, bool metric_no_threshold, int runtime, constchar *user_requested_cpu_list, bool system_wide, struct metric *root_metric, conststruct visited_metric *visited, conststruct pmu_metrics_table *table)
{ conststruct visited_metric *vm; int ret; bool is_root = !root_metric; constchar *expr; struct visited_metric visited_node = {
.name = pm->metric_name,
.parent = visited,
};
for (vm = visited; vm; vm = vm->parent) { if (!strcmp(pm->metric_name, vm->name)) {
pr_err("failed: recursion detected for %s\n", pm->metric_name); return -1;
}
}
if (is_root) { /* * This metric is the root of a tree and may reference other * metrics that are added recursively.
*/
root_metric = metric__new(pm, modifier, metric_no_group, metric_no_threshold,
runtime, user_requested_cpu_list, system_wide); if (!root_metric) return -ENOMEM;
} else { int cnt = 0;
/* * This metric was referenced in a metric higher in the * tree. Check if the same metric is already resolved in the * metric_refs list.
*/ if (root_metric->metric_refs) { for (; root_metric->metric_refs[cnt].metric_name; cnt++) { if (!strcmp(pm->metric_name,
root_metric->metric_refs[cnt].metric_name)) return 0;
}
}
/* Create reference. Need space for the entry and the terminator. */
root_metric->metric_refs = realloc(root_metric->metric_refs,
(cnt + 2) * sizeof(struct metric_ref)); if (!root_metric->metric_refs) return -ENOMEM;
/* * Intentionally passing just const char pointers, * from 'pe' object, so they never go away. We don't * need to change them, so there's no need to create * our own copy.
*/
root_metric->metric_refs[cnt].metric_name = pm->metric_name;
root_metric->metric_refs[cnt].metric_expr = pm->metric_expr;
/* * For both the parent and referenced metrics, we parse * all the metric's IDs and add it to the root context.
*/
ret = 0;
expr = pm->metric_expr; if (is_root && pm->metric_threshold) { /* * Threshold expressions are built off the actual metric. Switch * to use that in case of additional necessary events. Change * the visited node name to avoid this being flagged as * recursion. If the threshold events are disabled, just use the * metric's name as a reference. This allows metric threshold * computation if there are sufficient events.
*/
assert(strstr(pm->metric_threshold, pm->metric_name));
expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold;
visited_node.name = "__threshold__";
} if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) { /* Broken metric. */
ret = -EINVAL;
} if (!ret) { /* Resolve referenced metrics. */ struct perf_pmu *pmu;
/** * metricgroup__add_metric - Find and add a metric, or a metric group. * @pmu: The PMU name to search for metrics on, or "all" for all PMUs. * @metric_name: The name of the metric or metric group. For example, "IPC" * could be the name of a metric and "TopDownL1" the name of a * metric group. * @modifier: if non-null event modifiers like "u". * @metric_no_group: Should events written to events be grouped "{}" or * global. Grouping is the default but due to multiplexing the * user may override. * @user_requested_cpu_list: Command line specified CPUs to record on. * @system_wide: Are events for all processes recorded. * @metric_list: The list that the metric or metric group are added to. * @table: The table that is searched for metrics, most commonly the table for the * architecture perf is running upon.
*/ staticint metricgroup__add_metric(constchar *pmu, constchar *metric_name, constchar *modifier, bool metric_no_group, bool metric_no_threshold, constchar *user_requested_cpu_list, bool system_wide, struct list_head *metric_list, conststruct pmu_metrics_table *table)
{
LIST_HEAD(list); int ret; struct metricgroup__add_metric_data data = {
.list = &list,
.pmu = pmu,
.metric_name = metric_name,
.modifier = modifier,
.metric_no_group = metric_no_group,
.metric_no_threshold = metric_no_threshold,
.user_requested_cpu_list = user_requested_cpu_list,
.system_wide = system_wide,
.has_match = false,
};
/* * Iterate over all metrics seeing if metric matches either the * name or group. When it does add the metric to the list.
*/
ret = metricgroup__for_each_metric(table, metricgroup__add_metric_callback, &data); if (!ret && !data.has_match)
ret = -EINVAL;
/* * add to metric_list so that they can be released * even if it's failed
*/
list_splice(&list, metric_list); return ret;
}
/** * metricgroup__add_metric_list - Find and add metrics, or metric groups, * specified in a list. * @pmu: A pmu to restrict the metrics to, or "all" for all PMUS. * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1" * would match the IPC and CPI metrics, and TopDownL1 would match all * the metrics in the TopDownL1 group. * @metric_no_group: Should events written to events be grouped "{}" or * global. Grouping is the default but due to multiplexing the * user may override. * @user_requested_cpu_list: Command line specified CPUs to record on. * @system_wide: Are events for all processes recorded. * @metric_list: The list that metrics are added to. * @table: The table that is searched for metrics, most commonly the table for the * architecture perf is running upon.
*/ staticint metricgroup__add_metric_list(constchar *pmu, constchar *list, bool metric_no_group, bool metric_no_threshold, constchar *user_requested_cpu_list, bool system_wide, struct list_head *metric_list, conststruct pmu_metrics_table *table)
{ char *list_itr, *list_copy, *metric_name, *modifier; int ret, count = 0;
list_copy = strdup(list); if (!list_copy) return -ENOMEM;
list_itr = list_copy;
while ((metric_name = strsep(&list_itr, ",")) != NULL) {
modifier = strchr(metric_name, ':'); if (modifier)
*modifier++ = '\0';
ret = metricgroup__add_metric(pmu, metric_name, modifier,
metric_no_group, metric_no_threshold,
user_requested_cpu_list,
system_wide, metric_list, table); if (ret == -EINVAL)
pr_err("Cannot find metric or group `%s'\n", metric_name);
if (ret) break;
count++;
}
free(list_copy);
if (!ret) { /* * Warn about nmi_watchdog if any parsed metrics had the * NO_NMI_WATCHDOG constraint.
*/
metric__watchdog_constraint_hint(NULL, /*foot=*/true); /* No metrics. */ if (count == 0) return -EINVAL;
} return ret;
}
/** * find_tool_events - Search for the pressence of tool events in metric_list. * @metric_list: List to take metrics from. * @tool_events: Array of false values, indices corresponding to tool events set * to true if tool event is found.
*/ staticvoid find_tool_events(conststruct list_head *metric_list, bool tool_events[TOOL_PMU__EVENT_MAX])
{ struct metric *m;
/** * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events * metric IDs, as the IDs are held in a set, * duplicates will be removed. * @metric_list: List to take metrics from. * @combined: Out argument for result.
*/ staticint build_combined_expr_ctx(conststruct list_head *metric_list, struct expr_parse_ctx **combined)
{ struct hashmap_entry *cur;
size_t bkt; struct metric *m; char *dup; int ret;
*combined = expr__ctx_new(); if (!*combined) return -ENOMEM;
list_for_each_entry(m, metric_list, nd) { if (!m->group_events && !m->modifier) {
hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
dup = strdup(cur->pkey); if (!dup) {
ret = -ENOMEM; goto err_out;
}
ret = expr__add_id(*combined, dup); if (ret) goto err_out;
}
}
} return 0;
err_out:
expr__ctx_free(*combined);
*combined = NULL; return ret;
}
/** * parse_ids - Build the event string for the ids and parse them creating an * evlist. The encoded metric_ids are decoded. * @metric_no_merge: is metric sharing explicitly disabled. * @fake_pmu: use a fake PMU when testing metrics not supported by the current CPU. * @ids: the event identifiers parsed from a metric. * @modifier: any modifiers added to the events. * @group_events: should events be placed in a weak group. * @tool_events: entries set true if the tool event of index could be present in * the overall list of metrics. * @out_evlist: the created list of events.
*/ staticint parse_ids(bool metric_no_merge, bool fake_pmu, struct expr_parse_ctx *ids, constchar *modifier, bool group_events, constbool tool_events[TOOL_PMU__EVENT_MAX], struct evlist **out_evlist)
{ struct parse_events_error parse_error; struct evlist *parsed_evlist; struct strbuf events = STRBUF_INIT; int ret;
*out_evlist = NULL; if (!metric_no_merge || hashmap__size(ids->ids) == 0) { bool added_event = false; int i; /* * We may fail to share events between metrics because a tool * event isn't present in one metric. For example, a ratio of * cache misses doesn't need duration_time but the same events * may be used for a misses per second. Events without sharing * implies multiplexing, that is best avoided, so place * all tool events in every group. * * Also, there may be no ids/events in the expression parsing * context because of constant evaluation, e.g.: * event1 if #smt_on else 0 * Add a tool event to avoid a parse error on an empty string.
*/
tool_pmu__for_each_event(i) { if (tool_events[i]) { char *tmp = strdup(tool_pmu__event_to_str(i));
if (combined_evlist && !m->group_events) {
metric_evlist = combined_evlist;
} elseif (!metric_no_merge) { /* * See if the IDs for this metric are a subset of an * earlier metric.
*/
list_for_each_entry(n, &metric_list, nd) { if (m == n) break;
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.