|
@@ -1219,13 +1219,16 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
|
|
|
|
|
|
int parse_events_add_pmu(struct parse_events_state *parse_state,
|
|
|
struct list_head *list, char *name,
|
|
|
- struct list_head *head_config, bool auto_merge_stats)
|
|
|
+ struct list_head *head_config,
|
|
|
+ bool auto_merge_stats,
|
|
|
+ bool use_alias)
|
|
|
{
|
|
|
struct perf_event_attr attr;
|
|
|
struct perf_pmu_info info;
|
|
|
struct perf_pmu *pmu;
|
|
|
struct perf_evsel *evsel;
|
|
|
struct parse_events_error *err = parse_state->error;
|
|
|
+ bool use_uncore_alias;
|
|
|
LIST_HEAD(config_terms);
|
|
|
|
|
|
pmu = perf_pmu__find(name);
|
|
@@ -1244,11 +1247,14 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
|
}
|
|
|
|
|
|
+ use_uncore_alias = (pmu->is_uncore && use_alias);
|
|
|
+
|
|
|
if (!head_config) {
|
|
|
attr.type = pmu->type;
|
|
|
evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL, auto_merge_stats);
|
|
|
if (evsel) {
|
|
|
evsel->pmu_name = name;
|
|
|
+ evsel->use_uncore_alias = use_uncore_alias;
|
|
|
return 0;
|
|
|
} else {
|
|
|
return -ENOMEM;
|
|
@@ -1282,6 +1288,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
|
|
|
evsel->metric_expr = info.metric_expr;
|
|
|
evsel->metric_name = info.metric_name;
|
|
|
evsel->pmu_name = name;
|
|
|
+ evsel->use_uncore_alias = use_uncore_alias;
|
|
|
}
|
|
|
|
|
|
return evsel ? 0 : -ENOMEM;
|
|
@@ -1317,7 +1324,8 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
|
|
|
list_add_tail(&term->list, head);
|
|
|
|
|
|
if (!parse_events_add_pmu(parse_state, list,
|
|
|
- pmu->name, head, true)) {
|
|
|
+ pmu->name, head,
|
|
|
+ true, true)) {
|
|
|
pr_debug("%s -> %s/%s/\n", str,
|
|
|
pmu->name, alias->str);
|
|
|
ok++;
|
|
@@ -1339,7 +1347,120 @@ int parse_events__modifier_group(struct list_head *list,
|
|
|
return parse_events__modifier_event(list, event_mod, true);
|
|
|
}
|
|
|
|
|
|
-void parse_events__set_leader(char *name, struct list_head *list)
|
|
|
+/*
|
|
|
+ * Check if the two uncore PMUs are from the same uncore block
|
|
|
+ * The format of the uncore PMU name is uncore_#blockname_#pmuidx
|
|
|
+ */
|
|
|
+static bool is_same_uncore_block(const char *pmu_name_a, const char *pmu_name_b)
|
|
|
+{
|
|
|
+ char *end_a, *end_b;
|
|
|
+
|
|
|
+ end_a = strrchr(pmu_name_a, '_');
|
|
|
+ end_b = strrchr(pmu_name_b, '_');
|
|
|
+
|
|
|
+ if (!end_a || !end_b)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if ((end_a - pmu_name_a) != (end_b - pmu_name_b))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return (strncmp(pmu_name_a, pmu_name_b, end_a - pmu_name_a) == 0);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+parse_events__set_leader_for_uncore_aliase(char *name, struct list_head *list,
|
|
|
+ struct parse_events_state *parse_state)
|
|
|
+{
|
|
|
+ struct perf_evsel *evsel, *leader;
|
|
|
+ uintptr_t *leaders;
|
|
|
+ bool is_leader = true;
|
|
|
+ int i, nr_pmu = 0, total_members, ret = 0;
|
|
|
+
|
|
|
+ leader = list_first_entry(list, struct perf_evsel, node);
|
|
|
+ evsel = list_last_entry(list, struct perf_evsel, node);
|
|
|
+ total_members = evsel->idx - leader->idx + 1;
|
|
|
+
|
|
|
+ leaders = calloc(total_members, sizeof(uintptr_t));
|
|
|
+ if (WARN_ON(!leaders))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Going through the whole group and doing sanity check.
|
|
|
+ * All members must use alias, and be from the same uncore block.
|
|
|
+ * Also, storing the leader events in an array.
|
|
|
+ */
|
|
|
+ __evlist__for_each_entry(list, evsel) {
|
|
|
+
|
|
|
+ /* Only split the uncore group which members use alias */
|
|
|
+ if (!evsel->use_uncore_alias)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* The events must be from the same uncore block */
|
|
|
+ if (!is_same_uncore_block(leader->pmu_name, evsel->pmu_name))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (!is_leader)
|
|
|
+ continue;
|
|
|
+ /*
|
|
|
+ * If the event's PMU name starts to repeat, it must be a new
|
|
|
+ * event. That can be used to distinguish the leader from
|
|
|
+ * other members, even they have the same event name.
|
|
|
+ */
|
|
|
+ if ((leader != evsel) && (leader->pmu_name == evsel->pmu_name)) {
|
|
|
+ is_leader = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* The name is always alias name */
|
|
|
+ WARN_ON(strcmp(leader->name, evsel->name));
|
|
|
+
|
|
|
+ /* Store the leader event for each PMU */
|
|
|
+ leaders[nr_pmu++] = (uintptr_t) evsel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* only one event alias */
|
|
|
+ if (nr_pmu == total_members) {
|
|
|
+ parse_state->nr_groups--;
|
|
|
+ goto handled;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * An uncore event alias is a joint name which means the same event
|
|
|
+ * runs on all PMUs of a block.
|
|
|
+ * Perf doesn't support mixed events from different PMUs in the same
|
|
|
+ * group. The big group has to be split into multiple small groups
|
|
|
+ * which only include the events from the same PMU.
|
|
|
+ *
|
|
|
+ * Here the uncore event aliases must be from the same uncore block.
|
|
|
+ * The number of PMUs must be same for each alias. The number of new
|
|
|
+ * small groups equals to the number of PMUs.
|
|
|
+ * Setting the leader event for corresponding members in each group.
|
|
|
+ */
|
|
|
+ i = 0;
|
|
|
+ __evlist__for_each_entry(list, evsel) {
|
|
|
+ if (i >= nr_pmu)
|
|
|
+ i = 0;
|
|
|
+ evsel->leader = (struct perf_evsel *) leaders[i++];
|
|
|
+ }
|
|
|
+
|
|
|
+ /* The number of members and group name are same for each group */
|
|
|
+ for (i = 0; i < nr_pmu; i++) {
|
|
|
+ evsel = (struct perf_evsel *) leaders[i];
|
|
|
+ evsel->nr_members = total_members / nr_pmu;
|
|
|
+ evsel->group_name = name ? strdup(name) : NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Take the new small groups into account */
|
|
|
+ parse_state->nr_groups += nr_pmu - 1;
|
|
|
+
|
|
|
+handled:
|
|
|
+ ret = 1;
|
|
|
+out:
|
|
|
+ free(leaders);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void parse_events__set_leader(char *name, struct list_head *list,
|
|
|
+ struct parse_events_state *parse_state)
|
|
|
{
|
|
|
struct perf_evsel *leader;
|
|
|
|
|
@@ -1348,6 +1469,9 @@ void parse_events__set_leader(char *name, struct list_head *list)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (parse_events__set_leader_for_uncore_aliase(name, list, parse_state))
|
|
|
+ return;
|
|
|
+
|
|
|
__perf_evlist__set_leader(list);
|
|
|
leader = list_entry(list->next, struct perf_evsel, node);
|
|
|
leader->group_name = name ? strdup(name) : NULL;
|