diff options
| -rw-r--r-- | tools/perf/util/evsel.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.c | 130 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.h | 7 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.y | 8 |
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 | ||
| 1220 | int parse_events_add_pmu(struct parse_events_state *parse_state, | 1220 | int 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 | ||
| 1342 | void 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 | */ | ||
| 1354 | static 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 | |||
| 1370 | static int | ||
| 1371 | parse_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 | |||
| 1455 | handled: | ||
| 1456 | ret = 1; | ||
| 1457 | out: | ||
| 1458 | free(leaders); | ||
| 1459 | return ret; | ||
| 1460 | } | ||
| 1461 | |||
| 1462 | void 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); |
| 168 | int parse_events_add_pmu(struct parse_events_state *parse_state, | 168 | int 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 | ||
| 172 | int parse_events_multi_pmu_add(struct parse_events_state *parse_state, | 174 | int 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 | ||
| 179 | enum perf_pmu_event_symbol_type | 181 | enum perf_pmu_event_symbol_type |
| 180 | perf_pmu__parse_check(const char *name); | 182 | perf_pmu__parse_check(const char *name); |
| 181 | void parse_events__set_leader(char *name, struct list_head *list); | 183 | void parse_events__set_leader(char *name, struct list_head *list, |
| 184 | struct parse_events_state *parse_state); | ||
| 182 | void parse_events_update_lists(struct list_head *list_event, | 185 | void parse_events_update_lists(struct list_head *list_event, |
| 183 | struct list_head *list_all); | 186 | struct list_head *list_all); |
| 184 | void parse_events_evlist_error(struct parse_events_state *parse_state, | 187 | void 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 | } |
