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);
                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;
                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;
                                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++;
        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;
 
                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;
 
        struct list_head *list = $3;
 
        inc_group_count(list, _parse_state);
-       parse_events__set_leader($1, list);
+       parse_events__set_leader($1, list, _parse_state);
        $$ = list;
 }
 |
        struct list_head *list = $2;
 
        inc_group_count(list, _parse_state);
-       parse_events__set_leader(NULL, list);
+       parse_events__set_leader(NULL, list, _parse_state);
        $$ = list;
 }
 
                YYABORT;
 
        ALLOC_LIST(list);
-       if (parse_events_add_pmu(_parse_state, list, $1, $2, false)) {
+       if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) {
                struct perf_pmu *pmu = NULL;
                int ok = 0;
                char *pattern;
                                        free(pattern);
                                        YYABORT;
                                }
-                               if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true))
+                               if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
                                        ok++;
                                parse_events_terms__delete(terms);
                        }