aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKan Liang <kan.liang@linux.intel.com>2018-05-07 17:13:43 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2018-05-30 09:40:44 -0400
commit369b230806a9f3ff691466d54795e5e3ff3f8f5c (patch)
tree484483ca126c9f11ed6da1dc50ffbcac192cd0a3
parentf3903c9161f0d636a7b0ff03841628928457e64c (diff)
perf parse-events: Handle uncore event aliases in small groups properly
Perf stat doesn't count the uncore event aliases from the same uncore block in a group, for example: perf stat -e '{unc_m_cas_count.all,unc_m_clockticks}' -a -I 1000 # time counts unit events 1.000447342 <not counted> unc_m_cas_count.all 1.000447342 <not counted> unc_m_clockticks 2.000740654 <not counted> unc_m_cas_count.all 2.000740654 <not counted> unc_m_clockticks The output is very misleading. It gives a wrong impression that the uncore event doesn't work. An uncore block could be composed by several PMUs. 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. It is wrong to put uncore event aliases in a big group. The right way is to split the big group into multiple small groups which only include the events from the same PMU. Only uncore event aliases from the same uncore block should be specially handled here. It doesn't make sense to mix the uncore events with other uncore events from different blocks or even core events in a group. With the patch: # time counts unit events 1.001557653 140,833 unc_m_cas_count.all 1.001557653 1,330,231,332 unc_m_clockticks 2.002709483 85,007 unc_m_cas_count.all 2.002709483 1,429,494,563 unc_m_clockticks Reported-by: Andi Kleen <ak@linux.intel.com> Signed-off-by: Kan Liang <kan.liang@linux.intel.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Cc: Agustin Vega-Frias <agustinv@codeaurora.org> Cc: Ganapatrao Kulkarni <ganapatrao.kulkarni@cavium.com> Cc: Jin Yao <yao.jin@linux.intel.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Shaokun Zhang <zhangshaokun@hisilicon.com> Cc: Will Deacon <will.deacon@arm.com> Link: http://lkml.kernel.org/r/1525727623-19768-1-git-send-email-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/util/evsel.h1
-rw-r--r--tools/perf/util/parse-events.c130
-rw-r--r--tools/perf/util/parse-events.h7
-rw-r--r--tools/perf/util/parse-events.y8
4 files changed, 137 insertions, 9 deletions
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 92ec009a292d..b13f5f234c8f 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -127,6 +127,7 @@ struct perf_evsel {
127 bool precise_max; 127 bool precise_max;
128 bool ignore_missing_thread; 128 bool ignore_missing_thread;
129 bool forced_leader; 129 bool forced_leader;
130 bool use_uncore_alias;
130 /* parse modifier helper */ 131 /* parse modifier helper */
131 int exclude_GH; 132 int exclude_GH;
132 int nr_members; 133 int nr_members;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index b8b8a9558d32..2fc4ee8b86c1 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1219,13 +1219,16 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
1219 1219
1220int parse_events_add_pmu(struct parse_events_state *parse_state, 1220int parse_events_add_pmu(struct parse_events_state *parse_state,
1221 struct list_head *list, char *name, 1221 struct list_head *list, char *name,
1222 struct list_head *head_config, bool auto_merge_stats) 1222 struct list_head *head_config,
1223 bool auto_merge_stats,
1224 bool use_alias)
1223{ 1225{
1224 struct perf_event_attr attr; 1226 struct perf_event_attr attr;
1225 struct perf_pmu_info info; 1227 struct perf_pmu_info info;
1226 struct perf_pmu *pmu; 1228 struct perf_pmu *pmu;
1227 struct perf_evsel *evsel; 1229 struct perf_evsel *evsel;
1228 struct parse_events_error *err = parse_state->error; 1230 struct parse_events_error *err = parse_state->error;
1231 bool use_uncore_alias;
1229 LIST_HEAD(config_terms); 1232 LIST_HEAD(config_terms);
1230 1233
1231 pmu = perf_pmu__find(name); 1234 pmu = perf_pmu__find(name);
@@ -1244,11 +1247,14 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
1244 memset(&attr, 0, sizeof(attr)); 1247 memset(&attr, 0, sizeof(attr));
1245 } 1248 }
1246 1249
1250 use_uncore_alias = (pmu->is_uncore && use_alias);
1251
1247 if (!head_config) { 1252 if (!head_config) {
1248 attr.type = pmu->type; 1253 attr.type = pmu->type;
1249 evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL, auto_merge_stats); 1254 evsel = __add_event(list, &parse_state->idx, &attr, NULL, pmu, NULL, auto_merge_stats);
1250 if (evsel) { 1255 if (evsel) {
1251 evsel->pmu_name = name; 1256 evsel->pmu_name = name;
1257 evsel->use_uncore_alias = use_uncore_alias;
1252 return 0; 1258 return 0;
1253 } else { 1259 } else {
1254 return -ENOMEM; 1260 return -ENOMEM;
@@ -1282,6 +1288,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
1282 evsel->metric_expr = info.metric_expr; 1288 evsel->metric_expr = info.metric_expr;
1283 evsel->metric_name = info.metric_name; 1289 evsel->metric_name = info.metric_name;
1284 evsel->pmu_name = name; 1290 evsel->pmu_name = name;
1291 evsel->use_uncore_alias = use_uncore_alias;
1285 } 1292 }
1286 1293
1287 return evsel ? 0 : -ENOMEM; 1294 return evsel ? 0 : -ENOMEM;
@@ -1317,7 +1324,8 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
1317 list_add_tail(&term->list, head); 1324 list_add_tail(&term->list, head);
1318 1325
1319 if (!parse_events_add_pmu(parse_state, list, 1326 if (!parse_events_add_pmu(parse_state, list,
1320 pmu->name, head, true)) { 1327 pmu->name, head,
1328 true, true)) {
1321 pr_debug("%s -> %s/%s/\n", str, 1329 pr_debug("%s -> %s/%s/\n", str,
1322 pmu->name, alias->str); 1330 pmu->name, alias->str);
1323 ok++; 1331 ok++;
@@ -1339,7 +1347,120 @@ int parse_events__modifier_group(struct list_head *list,
1339 return parse_events__modifier_event(list, event_mod, true); 1347 return parse_events__modifier_event(list, event_mod, true);
1340} 1348}
1341 1349
1342void parse_events__set_leader(char *name, struct list_head *list) 1350/*
1351 * Check if the two uncore PMUs are from the same uncore block
1352 * The format of the uncore PMU name is uncore_#blockname_#pmuidx
1353 */
1354static bool is_same_uncore_block(const char *pmu_name_a, const char *pmu_name_b)
1355{
1356 char *end_a, *end_b;
1357
1358 end_a = strrchr(pmu_name_a, '_');
1359 end_b = strrchr(pmu_name_b, '_');
1360
1361 if (!end_a || !end_b)
1362 return false;
1363
1364 if ((end_a - pmu_name_a) != (end_b - pmu_name_b))
1365 return false;
1366
1367 return (strncmp(pmu_name_a, pmu_name_b, end_a - pmu_name_a) == 0);
1368}
1369
1370static int
1371parse_events__set_leader_for_uncore_aliase(char *name, struct list_head *list,
1372 struct parse_events_state *parse_state)
1373{
1374 struct perf_evsel *evsel, *leader;
1375 uintptr_t *leaders;
1376 bool is_leader = true;
1377 int i, nr_pmu = 0, total_members, ret = 0;
1378
1379 leader = list_first_entry(list, struct perf_evsel, node);
1380 evsel = list_last_entry(list, struct perf_evsel, node);
1381 total_members = evsel->idx - leader->idx + 1;
1382
1383 leaders = calloc(total_members, sizeof(uintptr_t));
1384 if (WARN_ON(!leaders))
1385 return 0;
1386
1387 /*
1388 * Going through the whole group and doing sanity check.
1389 * All members must use alias, and be from the same uncore block.
1390 * Also, storing the leader events in an array.
1391 */
1392 __evlist__for_each_entry(list, evsel) {
1393
1394 /* Only split the uncore group which members use alias */
1395 if (!evsel->use_uncore_alias)
1396 goto out;
1397
1398 /* The events must be from the same uncore block */
1399 if (!is_same_uncore_block(leader->pmu_name, evsel->pmu_name))
1400 goto out;
1401
1402 if (!is_leader)
1403 continue;
1404 /*
1405 * If the event's PMU name starts to repeat, it must be a new
1406 * event. That can be used to distinguish the leader from
1407 * other members, even they have the same event name.
1408 */
1409 if ((leader != evsel) && (leader->pmu_name == evsel->pmu_name)) {
1410 is_leader = false;
1411 continue;
1412 }
1413 /* The name is always alias name */
1414 WARN_ON(strcmp(leader->name, evsel->name));
1415
1416 /* Store the leader event for each PMU */
1417 leaders[nr_pmu++] = (uintptr_t) evsel;
1418 }
1419
1420 /* only one event alias */
1421 if (nr_pmu == total_members) {
1422 parse_state->nr_groups--;
1423 goto handled;
1424 }
1425
1426 /*
1427 * An uncore event alias is a joint name which means the same event
1428 * runs on all PMUs of a block.
1429 * Perf doesn't support mixed events from different PMUs in the same
1430 * group. The big group has to be split into multiple small groups
1431 * which only include the events from the same PMU.
1432 *
1433 * Here the uncore event aliases must be from the same uncore block.
1434 * The number of PMUs must be same for each alias. The number of new
1435 * small groups equals to the number of PMUs.
1436 * Setting the leader event for corresponding members in each group.
1437 */
1438 i = 0;
1439 __evlist__for_each_entry(list, evsel) {
1440 if (i >= nr_pmu)
1441 i = 0;
1442 evsel->leader = (struct perf_evsel *) leaders[i++];
1443 }
1444
1445 /* The number of members and group name are same for each group */
1446 for (i = 0; i < nr_pmu; i++) {
1447 evsel = (struct perf_evsel *) leaders[i];
1448 evsel->nr_members = total_members / nr_pmu;
1449 evsel->group_name = name ? strdup(name) : NULL;
1450 }
1451
1452 /* Take the new small groups into account */
1453 parse_state->nr_groups += nr_pmu - 1;
1454
1455handled:
1456 ret = 1;
1457out:
1458 free(leaders);
1459 return ret;
1460}
1461
1462void parse_events__set_leader(char *name, struct list_head *list,
1463 struct parse_events_state *parse_state)
1343{ 1464{
1344 struct perf_evsel *leader; 1465 struct perf_evsel *leader;
1345 1466
@@ -1348,6 +1469,9 @@ void parse_events__set_leader(char *name, struct list_head *list)
1348 return; 1469 return;
1349 } 1470 }
1350 1471
1472 if (parse_events__set_leader_for_uncore_aliase(name, list, parse_state))
1473 return;
1474
1351 __perf_evlist__set_leader(list); 1475 __perf_evlist__set_leader(list);
1352 leader = list_entry(list->next, struct perf_evsel, node); 1476 leader = list_entry(list->next, struct perf_evsel, node);
1353 leader->group_name = name ? strdup(name) : NULL; 1477 leader->group_name = name ? strdup(name) : NULL;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 5015cfd58277..4473dac27aee 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -167,7 +167,9 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
167 void *ptr, char *type, u64 len); 167 void *ptr, char *type, u64 len);
168int parse_events_add_pmu(struct parse_events_state *parse_state, 168int parse_events_add_pmu(struct parse_events_state *parse_state,
169 struct list_head *list, char *name, 169 struct list_head *list, char *name,
170 struct list_head *head_config, bool auto_merge_stats); 170 struct list_head *head_config,
171 bool auto_merge_stats,
172 bool use_alias);
171 173
172int parse_events_multi_pmu_add(struct parse_events_state *parse_state, 174int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
173 char *str, 175 char *str,
@@ -178,7 +180,8 @@ int parse_events_copy_term_list(struct list_head *old,
178 180
179enum perf_pmu_event_symbol_type 181enum perf_pmu_event_symbol_type
180perf_pmu__parse_check(const char *name); 182perf_pmu__parse_check(const char *name);
181void parse_events__set_leader(char *name, struct list_head *list); 183void parse_events__set_leader(char *name, struct list_head *list,
184 struct parse_events_state *parse_state);
182void parse_events_update_lists(struct list_head *list_event, 185void parse_events_update_lists(struct list_head *list_event,
183 struct list_head *list_all); 186 struct list_head *list_all);
184void parse_events_evlist_error(struct parse_events_state *parse_state, 187void parse_events_evlist_error(struct parse_events_state *parse_state,
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 7afeb80cc39e..e37608a87dba 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -161,7 +161,7 @@ PE_NAME '{' events '}'
161 struct list_head *list = $3; 161 struct list_head *list = $3;
162 162
163 inc_group_count(list, _parse_state); 163 inc_group_count(list, _parse_state);
164 parse_events__set_leader($1, list); 164 parse_events__set_leader($1, list, _parse_state);
165 $$ = list; 165 $$ = list;
166} 166}
167| 167|
@@ -170,7 +170,7 @@ PE_NAME '{' events '}'
170 struct list_head *list = $2; 170 struct list_head *list = $2;
171 171
172 inc_group_count(list, _parse_state); 172 inc_group_count(list, _parse_state);
173 parse_events__set_leader(NULL, list); 173 parse_events__set_leader(NULL, list, _parse_state);
174 $$ = list; 174 $$ = list;
175} 175}
176 176
@@ -232,7 +232,7 @@ PE_NAME opt_event_config
232 YYABORT; 232 YYABORT;
233 233
234 ALLOC_LIST(list); 234 ALLOC_LIST(list);
235 if (parse_events_add_pmu(_parse_state, list, $1, $2, false)) { 235 if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) {
236 struct perf_pmu *pmu = NULL; 236 struct perf_pmu *pmu = NULL;
237 int ok = 0; 237 int ok = 0;
238 char *pattern; 238 char *pattern;
@@ -251,7 +251,7 @@ PE_NAME opt_event_config
251 free(pattern); 251 free(pattern);
252 YYABORT; 252 YYABORT;
253 } 253 }
254 if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true)) 254 if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
255 ok++; 255 ok++;
256 parse_events_terms__delete(terms); 256 parse_events_terms__delete(terms);
257 } 257 }