Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf evsel: Add alternate_hw_config and use in evsel__match

There are cases where we want to match events like instructions and
cycles with legacy hardware values, in particular in stat-shadow's
hard coded metrics. An evsel's name isn't a good point of reference as
it gets altered, strstr would be too imprecise and re-parsing the
event from its name is silly. Instead, hold the legacy hardware event
name, determined during parsing, in the evsel for this matching case.

Inline evsel__match2 that is only used in builtin-diff.

Acked-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Acked-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: James Clark <james.clark@linaro.org>
Cc: Yang Jihong <yangjihong@bytedance.com>
Cc: Dominique Martinet <asmadeus@codewreck.org>
Cc: Colin Ian King <colin.i.king@gmail.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Yunseong Kim <yskelg@gmail.com>
Cc: Ze Gao <zegao2021@gmail.com>
Cc: Yicong Yang <yangyicong@hisilicon.com>
Cc: Weilin Wang <weilin.wang@intel.com>
Cc: Will Deacon <will@kernel.org>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Yang Li <yang.lee@linux.alibaba.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: ak@linux.intel.com
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sun Haiyong <sunhaiyong@loongson.cn>
Cc: John Garry <john.g.garry@oracle.com>
Link: https://lore.kernel.org/r/20240926144851.245903-2-james.clark@linaro.org
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
22a4db3c 7e73ea40

+77 -46
+3 -3
tools/perf/builtin-diff.c
··· 469 469 470 470 static struct perf_diff pdiff; 471 471 472 - static struct evsel *evsel_match(struct evsel *evsel, 473 - struct evlist *evlist) 472 + static struct evsel *evsel_match(struct evsel *evsel, struct evlist *evlist) 474 473 { 475 474 struct evsel *e; 476 475 477 476 evlist__for_each_entry(evlist, e) { 478 - if (evsel__match2(evsel, e)) 477 + if ((evsel->core.attr.type == e->core.attr.type) && 478 + (evsel->core.attr.config == e->core.attr.config)) 479 479 return e; 480 480 } 481 481
+21
tools/perf/util/evsel.c
··· 299 299 evsel->pmu_name = NULL; 300 300 evsel->group_pmu_name = NULL; 301 301 evsel->skippable = false; 302 + evsel->alternate_hw_config = PERF_COUNT_HW_MAX; 302 303 } 303 304 304 305 struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx) ··· 445 444 446 445 if (evsel__copy_config_terms(evsel, orig) < 0) 447 446 goto out_err; 447 + 448 + evsel->alternate_hw_config = orig->alternate_hw_config; 448 449 449 450 return evsel; 450 451 ··· 1846 1843 count->ena = count->run = delta_start; 1847 1844 count->lost = 0; 1848 1845 return 0; 1846 + } 1847 + 1848 + bool __evsel__match(const struct evsel *evsel, u32 type, u64 config) 1849 + { 1850 + 1851 + u32 e_type = evsel->core.attr.type; 1852 + u64 e_config = evsel->core.attr.config; 1853 + 1854 + if (e_type != type) { 1855 + return type == PERF_TYPE_HARDWARE && evsel->pmu && evsel->pmu->is_core && 1856 + evsel->alternate_hw_config == config; 1857 + } 1858 + 1859 + if ((type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE) && 1860 + perf_pmus__supports_extended_type()) 1861 + e_config &= PERF_HW_EVENT_MASK; 1862 + 1863 + return e_config == config; 1849 1864 } 1850 1865 1851 1866 int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread)
+2 -17
tools/perf/util/evsel.h
··· 102 102 int bpf_fd; 103 103 struct bpf_object *bpf_obj; 104 104 struct list_head config_terms; 105 + u64 alternate_hw_config; 105 106 }; 106 107 107 108 /* ··· 394 393 struct tep_format_field *evsel__field(struct evsel *evsel, const char *name); 395 394 struct tep_format_field *evsel__common_field(struct evsel *evsel, const char *name); 396 395 397 - static inline bool __evsel__match(const struct evsel *evsel, u32 type, u64 config) 398 - { 399 - if (evsel->core.attr.type != type) 400 - return false; 401 - 402 - if ((type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE) && 403 - perf_pmus__supports_extended_type()) 404 - return (evsel->core.attr.config & PERF_HW_EVENT_MASK) == config; 405 - 406 - return evsel->core.attr.config == config; 407 - } 396 + bool __evsel__match(const struct evsel *evsel, u32 type, u64 config); 408 397 409 398 #define evsel__match(evsel, t, c) __evsel__match(evsel, PERF_TYPE_##t, PERF_COUNT_##c) 410 - 411 - static inline bool evsel__match2(struct evsel *e1, struct evsel *e2) 412 - { 413 - return (e1->core.attr.type == e2->core.attr.type) && 414 - (e1->core.attr.config == e2->core.attr.config); 415 - } 416 399 417 400 int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread); 418 401
+37 -22
tools/perf/util/parse-events.c
··· 228 228 bool init_attr, 229 229 const char *name, const char *metric_id, struct perf_pmu *pmu, 230 230 struct list_head *config_terms, bool auto_merge_stats, 231 - struct perf_cpu_map *cpu_list) 231 + struct perf_cpu_map *cpu_list, u64 alternate_hw_config) 232 232 { 233 233 struct evsel *evsel; 234 234 struct perf_cpu_map *cpus = perf_cpu_map__is_empty(cpu_list) && pmu ? pmu->cpus : cpu_list; ··· 264 264 evsel->auto_merge_stats = auto_merge_stats; 265 265 evsel->pmu = pmu; 266 266 evsel->pmu_name = pmu ? strdup(pmu->name) : NULL; 267 + evsel->alternate_hw_config = alternate_hw_config; 267 268 268 269 if (name) 269 270 evsel->name = strdup(name); ··· 287 286 { 288 287 return __add_event(/*list=*/NULL, &idx, attr, /*init_attr=*/false, name, 289 288 metric_id, pmu, /*config_terms=*/NULL, 290 - /*auto_merge_stats=*/false, /*cpu_list=*/NULL); 289 + /*auto_merge_stats=*/false, /*cpu_list=*/NULL, 290 + /*alternate_hw_config=*/PERF_COUNT_HW_MAX); 291 291 } 292 292 293 293 static int add_event(struct list_head *list, int *idx, 294 294 struct perf_event_attr *attr, const char *name, 295 - const char *metric_id, struct list_head *config_terms) 295 + const char *metric_id, struct list_head *config_terms, 296 + u64 alternate_hw_config) 296 297 { 297 298 return __add_event(list, idx, attr, /*init_attr*/true, name, metric_id, 298 299 /*pmu=*/NULL, config_terms, 299 - /*auto_merge_stats=*/false, /*cpu_list=*/NULL) ? 0 : -ENOMEM; 300 + /*auto_merge_stats=*/false, /*cpu_list=*/NULL, 301 + alternate_hw_config) ? 0 : -ENOMEM; 300 302 } 301 303 302 304 static int add_event_tool(struct list_head *list, int *idx, ··· 319 315 evsel = __add_event(list, idx, &attr, /*init_attr=*/true, /*name=*/NULL, 320 316 /*metric_id=*/NULL, /*pmu=*/NULL, 321 317 /*config_terms=*/NULL, /*auto_merge_stats=*/false, 322 - cpu_list); 318 + cpu_list, 319 + /*alternate_hw_config=*/PERF_COUNT_HW_MAX); 323 320 perf_cpu_map__put(cpu_list); 324 321 if (!evsel) 325 322 return -ENOMEM; ··· 455 450 static int parse_events_add_pmu(struct parse_events_state *parse_state, 456 451 struct list_head *list, struct perf_pmu *pmu, 457 452 const struct parse_events_terms *const_parsed_terms, 458 - bool auto_merge_stats); 453 + bool auto_merge_stats, u64 alternate_hw_config); 459 454 460 455 int parse_events_add_cache(struct list_head *list, int *idx, const char *name, 461 456 struct parse_events_state *parse_state, ··· 481 476 */ 482 477 ret = parse_events_add_pmu(parse_state, list, pmu, 483 478 parsed_terms, 484 - perf_pmu__auto_merge_stats(pmu)); 479 + perf_pmu__auto_merge_stats(pmu), 480 + /*alternate_hw_config=*/PERF_COUNT_HW_MAX); 485 481 if (ret) 486 482 return ret; 487 483 continue; ··· 513 507 514 508 if (__add_event(list, idx, &attr, /*init_attr*/true, config_name ?: name, 515 509 metric_id, pmu, &config_terms, /*auto_merge_stats=*/false, 516 - /*cpu_list=*/NULL) == NULL) 510 + /*cpu_list=*/NULL, 511 + /*alternate_hw_config=*/PERF_COUNT_HW_MAX) == NULL) 517 512 return -ENOMEM; 518 513 519 514 free_config_terms(&config_terms); ··· 779 772 name = get_config_name(head_config); 780 773 781 774 return add_event(list, &parse_state->idx, &attr, name, /*mertic_id=*/NULL, 782 - &config_terms); 775 + &config_terms, /*alternate_hw_config=*/PERF_COUNT_HW_MAX); 783 776 } 784 777 785 778 static int check_type_val(struct parse_events_term *term, ··· 1079 1072 if (perf_pmu__have_event(pmu, term->config)) { 1080 1073 term->type_term = PARSE_EVENTS__TERM_TYPE_USER; 1081 1074 term->no_value = true; 1075 + term->alternate_hw_config = true; 1082 1076 } else { 1083 1077 attr->type = PERF_TYPE_HARDWARE; 1084 1078 attr->config = term->val.num; ··· 1392 1384 name = get_config_name(head_config); 1393 1385 metric_id = get_config_metric_id(head_config); 1394 1386 ret = __add_event(list, &parse_state->idx, &attr, /*init_attr*/true, name, 1395 - metric_id, pmu, &config_terms, /*auto_merge_stats=*/false, 1396 - /*cpu_list=*/NULL) ? 0 : -ENOMEM; 1387 + metric_id, pmu, &config_terms, /*auto_merge_stats=*/false, 1388 + /*cpu_list=*/NULL, /*alternate_hw_config=*/PERF_COUNT_HW_MAX 1389 + ) == NULL ? -ENOMEM : 0; 1397 1390 free_config_terms(&config_terms); 1398 1391 return ret; 1399 1392 } ··· 1452 1443 static int parse_events_add_pmu(struct parse_events_state *parse_state, 1453 1444 struct list_head *list, struct perf_pmu *pmu, 1454 1445 const struct parse_events_terms *const_parsed_terms, 1455 - bool auto_merge_stats) 1446 + bool auto_merge_stats, u64 alternate_hw_config) 1456 1447 { 1457 1448 struct perf_event_attr attr; 1458 1449 struct perf_pmu_info info; ··· 1489 1480 /*init_attr=*/true, /*name=*/NULL, 1490 1481 /*metric_id=*/NULL, pmu, 1491 1482 /*config_terms=*/NULL, auto_merge_stats, 1492 - /*cpu_list=*/NULL); 1483 + /*cpu_list=*/NULL, alternate_hw_config); 1493 1484 return evsel ? 0 : -ENOMEM; 1494 1485 } 1495 1486 ··· 1510 1501 1511 1502 /* Look for event names in the terms and rewrite into format based terms. */ 1512 1503 if (perf_pmu__check_alias(pmu, &parsed_terms, 1513 - &info, &alias_rewrote_terms, err)) { 1504 + &info, &alias_rewrote_terms, 1505 + &alternate_hw_config, err)) { 1514 1506 parse_events_terms__exit(&parsed_terms); 1515 1507 return -EINVAL; 1516 1508 } ··· 1556 1546 evsel = __add_event(list, &parse_state->idx, &attr, /*init_attr=*/true, 1557 1547 get_config_name(&parsed_terms), 1558 1548 get_config_metric_id(&parsed_terms), pmu, 1559 - &config_terms, auto_merge_stats, /*cpu_list=*/NULL); 1549 + &config_terms, auto_merge_stats, /*cpu_list=*/NULL, 1550 + alternate_hw_config); 1560 1551 if (!evsel) { 1561 1552 parse_events_terms__exit(&parsed_terms); 1562 1553 return -ENOMEM; ··· 1578 1567 } 1579 1568 1580 1569 int parse_events_multi_pmu_add(struct parse_events_state *parse_state, 1581 - const char *event_name, 1570 + const char *event_name, u64 hw_config, 1582 1571 const struct parse_events_terms *const_parsed_terms, 1583 1572 struct list_head **listp, void *loc_) 1584 1573 { ··· 1631 1620 1632 1621 auto_merge_stats = perf_pmu__auto_merge_stats(pmu); 1633 1622 if (!parse_events_add_pmu(parse_state, list, pmu, 1634 - &parsed_terms, auto_merge_stats)) { 1623 + &parsed_terms, auto_merge_stats, hw_config)) { 1635 1624 struct strbuf sb; 1636 1625 1637 1626 strbuf_init(&sb, /*hint=*/ 0); ··· 1644 1633 1645 1634 if (parse_state->fake_pmu) { 1646 1635 if (!parse_events_add_pmu(parse_state, list, perf_pmus__fake_pmu(), &parsed_terms, 1647 - /*auto_merge_stats=*/true)) { 1636 + /*auto_merge_stats=*/true, hw_config)) { 1648 1637 struct strbuf sb; 1649 1638 1650 1639 strbuf_init(&sb, /*hint=*/ 0); ··· 1685 1674 /* Attempt to add to list assuming event_or_pmu is a PMU name. */ 1686 1675 pmu = perf_pmus__find(event_or_pmu); 1687 1676 if (pmu && !parse_events_add_pmu(parse_state, *listp, pmu, const_parsed_terms, 1688 - /*auto_merge_stats=*/false)) 1677 + /*auto_merge_stats=*/false, 1678 + /*alternate_hw_config=*/PERF_COUNT_HW_MAX)) 1689 1679 return 0; 1690 1680 1691 1681 if (parse_state->fake_pmu) { 1692 1682 if (!parse_events_add_pmu(parse_state, *listp, perf_pmus__fake_pmu(), 1693 1683 const_parsed_terms, 1694 - /*auto_merge_stats=*/false)) 1684 + /*auto_merge_stats=*/false, 1685 + /*alternate_hw_config=*/PERF_COUNT_HW_MAX)) 1695 1686 return 0; 1696 1687 } 1697 1688 ··· 1706 1693 1707 1694 if (!parse_events_add_pmu(parse_state, *listp, pmu, 1708 1695 const_parsed_terms, 1709 - auto_merge_stats)) { 1696 + auto_merge_stats, 1697 + /*alternate_hw_config=*/PERF_COUNT_HW_MAX)) { 1710 1698 ok++; 1711 1699 parse_state->wild_card_pmus = true; 1712 1700 } ··· 1718 1704 1719 1705 /* Failure to add, assume event_or_pmu is an event name. */ 1720 1706 zfree(listp); 1721 - if (!parse_events_multi_pmu_add(parse_state, event_or_pmu, const_parsed_terms, listp, loc)) 1707 + if (!parse_events_multi_pmu_add(parse_state, event_or_pmu, PERF_COUNT_HW_MAX, 1708 + const_parsed_terms, listp, loc)) 1722 1709 return 0; 1723 1710 1724 1711 if (asprintf(&help, "Unable to find PMU or event on a PMU of '%s'", event_or_pmu) < 0)
+7 -1
tools/perf/util/parse-events.h
··· 127 127 * value is assumed to be 1. An event name also has no value. 128 128 */ 129 129 bool no_value; 130 + /** 131 + * @alternate_hw_config: config is the event name but num is an 132 + * alternate PERF_TYPE_HARDWARE config value which is often nice for the 133 + * sake of quick matching. 134 + */ 135 + bool alternate_hw_config; 130 136 }; 131 137 132 138 struct parse_events_error { ··· 244 238 struct perf_pmu *pmu); 245 239 246 240 int parse_events_multi_pmu_add(struct parse_events_state *parse_state, 247 - const char *event_name, 241 + const char *event_name, u64 hw_config, 248 242 const struct parse_events_terms *const_parsed_terms, 249 243 struct list_head **listp, void *loc); 250 244
+1 -1
tools/perf/util/parse-events.y
··· 292 292 struct list_head *list; 293 293 int err; 294 294 295 - err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list, &@1); 295 + err = parse_events_multi_pmu_add(_parse_state, $1, PERF_COUNT_HW_MAX, NULL, &list, &@1); 296 296 if (err < 0) { 297 297 struct parse_events_state *parse_state = _parse_state; 298 298 struct parse_events_error *error = parse_state->error;
+5 -1
tools/perf/util/pmu.c
··· 1606 1606 */ 1607 1607 int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_terms, 1608 1608 struct perf_pmu_info *info, bool *rewrote_terms, 1609 - struct parse_events_error *err) 1609 + u64 *alternate_hw_config, struct parse_events_error *err) 1610 1610 { 1611 1611 struct parse_events_term *term, *h; 1612 1612 struct perf_pmu_alias *alias; ··· 1638 1638 NULL); 1639 1639 return ret; 1640 1640 } 1641 + 1641 1642 *rewrote_terms = true; 1642 1643 ret = check_info_data(pmu, alias, info, err, term->err_term); 1643 1644 if (ret) ··· 1646 1645 1647 1646 if (alias->per_pkg) 1648 1647 info->per_pkg = true; 1648 + 1649 + if (term->alternate_hw_config) 1650 + *alternate_hw_config = term->val.num; 1649 1651 1650 1652 list_del_init(&term->list); 1651 1653 parse_events_term__delete(term);
+1 -1
tools/perf/util/pmu.h
··· 215 215 int perf_pmu__format_type(struct perf_pmu *pmu, const char *name); 216 216 int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_terms, 217 217 struct perf_pmu_info *info, bool *rewrote_terms, 218 - struct parse_events_error *err); 218 + u64 *alternate_hw_config, struct parse_events_error *err); 219 219 int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb); 220 220 221 221 void perf_pmu_format__set_value(void *format, int config, unsigned long *bits);