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

perf metrics: Wire up core_wide

Pass state necessary for core_wide into the expression parser. Add
system_wide and user_requested_cpu_list to perf_stat_config to make it
available at display time. evlist isn't used as the
evlist__create_maps, that computes user_requested_cpus, needs the list
of events which is generated by the metric.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Ahmad Yasin <ahmad.yasin@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Caleb Biggers <caleb.biggers@intel.com>
Cc: Florian Fischer <florian.fischer@muhq.space>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.garry@huawei.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Kshipra Bopardikar <kshipra.bopardikar@intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Miaoqian Lin <linmq006@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Perry Taylor <perry.taylor@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Link: https://lore.kernel.org/r/20220831174926.579643-7-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
1725e9cd a4b8cfca

+134 -43
+14
tools/perf/builtin-stat.c
··· 1805 1805 return metricgroup__parse_groups(evsel_list, "transaction", 1806 1806 stat_config.metric_no_group, 1807 1807 stat_config.metric_no_merge, 1808 + stat_config.user_requested_cpu_list, 1809 + stat_config.system_wide, 1808 1810 &stat_config.metric_events); 1809 1811 } 1810 1812 ··· 2443 2441 if ((stat_config.aggr_mode == AGGR_THREAD) && (target.system_wide)) 2444 2442 target.per_thread = true; 2445 2443 2444 + stat_config.system_wide = target.system_wide; 2445 + if (target.cpu_list) { 2446 + stat_config.user_requested_cpu_list = strdup(target.cpu_list); 2447 + if (!stat_config.user_requested_cpu_list) { 2448 + status = -ENOMEM; 2449 + goto out; 2450 + } 2451 + } 2452 + 2446 2453 /* 2447 2454 * Metric parsing needs to be delayed as metrics may optimize events 2448 2455 * knowing the target is system-wide. ··· 2460 2449 metricgroup__parse_groups(evsel_list, metrics, 2461 2450 stat_config.metric_no_group, 2462 2451 stat_config.metric_no_merge, 2452 + stat_config.user_requested_cpu_list, 2453 + stat_config.system_wide, 2463 2454 &stat_config.metric_events); 2464 2455 zfree(&metrics); 2465 2456 } ··· 2652 2639 iostat_release(evsel_list); 2653 2640 2654 2641 zfree(&stat_config.walltime_run); 2642 + zfree(&stat_config.user_requested_cpu_list); 2655 2643 2656 2644 if (smi_cost && smi_reset) 2657 2645 sysfs__write_int(FREEZE_ON_SMI_PATH, 0);
+12 -1
tools/perf/util/expr.c
··· 310 310 free(ctx); 311 311 return NULL; 312 312 } 313 + ctx->sctx.user_requested_cpu_list = NULL; 313 314 ctx->sctx.runtime = 0; 315 + ctx->sctx.system_wide = false; 314 316 315 317 return ctx; 316 318 } ··· 334 332 struct hashmap_entry *cur; 335 333 size_t bkt; 336 334 335 + if (!ctx) 336 + return; 337 + 338 + free(ctx->sctx.user_requested_cpu_list); 337 339 hashmap__for_each_entry(ctx->ids, cur, bkt) { 338 340 free((char *)cur->key); 339 341 free(cur->value); ··· 413 407 } 414 408 #endif 415 409 416 - double expr__get_literal(const char *literal) 410 + double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx) 417 411 { 418 412 static struct cpu_topology *topology; 419 413 double result = NAN; ··· 443 437 } 444 438 if (!strcasecmp("#smt_on", literal)) { 445 439 result = smt_on(topology) ? 1.0 : 0.0; 440 + goto out; 441 + } 442 + if (!strcmp("#core_wide", literal)) { 443 + result = core_wide(ctx->system_wide, ctx->user_requested_cpu_list, topology) 444 + ? 1.0 : 0.0; 446 445 goto out; 447 446 } 448 447 if (!strcmp("#num_packages", literal)) {
+3 -1
tools/perf/util/expr.h
··· 11 11 struct metric_ref; 12 12 13 13 struct expr_scanner_ctx { 14 + char *user_requested_cpu_list; 14 15 int runtime; 16 + bool system_wide; 15 17 }; 16 18 17 19 struct expr_parse_ctx { ··· 57 55 58 56 double expr_id_data__value(const struct expr_id_data *data); 59 57 double expr_id_data__source_count(const struct expr_id_data *data); 60 - double expr__get_literal(const char *literal); 58 + double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx); 61 59 62 60 #endif
+3 -3
tools/perf/util/expr.l
··· 79 79 return token; 80 80 } 81 81 82 - static int literal(yyscan_t scanner) 82 + static int literal(yyscan_t scanner, const struct expr_scanner_ctx *sctx) 83 83 { 84 84 YYSTYPE *yylval = expr_get_lval(scanner); 85 85 86 - yylval->num = expr__get_literal(expr_get_text(scanner)); 86 + yylval->num = expr__get_literal(expr_get_text(scanner), sctx); 87 87 if (isnan(yylval->num)) 88 88 return EXPR_ERROR; 89 89 ··· 108 108 if { return IF; } 109 109 else { return ELSE; } 110 110 source_count { return SOURCE_COUNT; } 111 - {literal} { return literal(yyscanner); } 111 + {literal} { return literal(yyscanner, sctx); } 112 112 {number} { return value(yyscanner); } 113 113 {symbol} { return str(yyscanner, ID, sctx->runtime); } 114 114 "|" { return '|'; }
+90 -35
tools/perf/util/metricgroup.c
··· 22 22 #include <linux/list_sort.h> 23 23 #include <linux/string.h> 24 24 #include <linux/zalloc.h> 25 + #include <perf/cpumap.h> 25 26 #include <subcmd/parse-options.h> 26 27 #include <api/fs/fs.h> 27 28 #include "util.h" ··· 190 189 return false; 191 190 } 192 191 192 + static void metric__free(struct metric *m) 193 + { 194 + if (!m) 195 + return; 196 + 197 + free(m->metric_refs); 198 + expr__ctx_free(m->pctx); 199 + free((char *)m->modifier); 200 + evlist__delete(m->evlist); 201 + free(m); 202 + } 203 + 193 204 static struct metric *metric__new(const struct pmu_event *pe, 194 205 const char *modifier, 195 206 bool metric_no_group, 196 - int runtime) 207 + int runtime, 208 + const char *user_requested_cpu_list, 209 + bool system_wide) 197 210 { 198 211 struct metric *m; 199 212 ··· 216 201 return NULL; 217 202 218 203 m->pctx = expr__ctx_new(); 219 - if (!m->pctx) { 220 - free(m); 221 - return NULL; 222 - } 204 + if (!m->pctx) 205 + goto out_err; 223 206 224 207 m->metric_name = pe->metric_name; 225 - m->modifier = modifier ? strdup(modifier) : NULL; 226 - if (modifier && !m->modifier) { 227 - expr__ctx_free(m->pctx); 228 - free(m); 229 - return NULL; 208 + m->modifier = NULL; 209 + if (modifier) { 210 + m->modifier = strdup(modifier); 211 + if (!m->modifier) 212 + goto out_err; 230 213 } 231 214 m->metric_expr = pe->metric_expr; 232 215 m->metric_unit = pe->unit; 216 + m->pctx->sctx.user_requested_cpu_list = NULL; 217 + if (user_requested_cpu_list) { 218 + m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list); 219 + if (!m->pctx->sctx.user_requested_cpu_list) 220 + goto out_err; 221 + } 233 222 m->pctx->sctx.runtime = runtime; 223 + m->pctx->sctx.system_wide = system_wide; 234 224 m->has_constraint = metric_no_group || metricgroup__has_constraint(pe); 235 225 m->metric_refs = NULL; 236 226 m->evlist = NULL; 237 227 238 228 return m; 239 - } 240 - 241 - static void metric__free(struct metric *m) 242 - { 243 - free(m->metric_refs); 244 - expr__ctx_free(m->pctx); 245 - free((char *)m->modifier); 246 - evlist__delete(m->evlist); 247 - free(m); 229 + out_err: 230 + metric__free(m); 231 + return NULL; 248 232 } 249 233 250 234 static bool contains_metric_id(struct evsel **metric_events, int num_events, ··· 888 874 int *ret; 889 875 bool *has_match; 890 876 bool metric_no_group; 877 + const char *user_requested_cpu_list; 878 + bool system_wide; 891 879 struct metric *root_metric; 892 880 const struct visited_metric *visited; 893 881 const struct pmu_events_table *table; ··· 903 887 const struct pmu_event *pe, 904 888 const char *modifier, 905 889 bool metric_no_group, 890 + const char *user_requested_cpu_list, 891 + bool system_wide, 906 892 struct metric *root_metric, 907 893 const struct visited_metric *visited, 908 894 const struct pmu_events_table *table); ··· 917 899 * @metric_no_group: Should events written to events be grouped "{}" or 918 900 * global. Grouping is the default but due to multiplexing the 919 901 * user may override. 902 + * @user_requested_cpu_list: Command line specified CPUs to record on. 903 + * @system_wide: Are events for all processes recorded. 920 904 * @root_metric: Metrics may reference other metrics to form a tree. In this 921 905 * case the root_metric holds all the IDs and a list of referenced 922 906 * metrics. When adding a root this argument is NULL. ··· 930 910 static int resolve_metric(struct list_head *metric_list, 931 911 const char *modifier, 932 912 bool metric_no_group, 913 + const char *user_requested_cpu_list, 914 + bool system_wide, 933 915 struct metric *root_metric, 934 916 const struct visited_metric *visited, 935 917 const struct pmu_events_table *table) ··· 978 956 */ 979 957 for (i = 0; i < pending_cnt; i++) { 980 958 ret = add_metric(metric_list, &pending[i].pe, modifier, metric_no_group, 981 - root_metric, visited, table); 959 + user_requested_cpu_list, system_wide, root_metric, visited, 960 + table); 982 961 if (ret) 983 962 break; 984 963 } ··· 997 974 * global. Grouping is the default but due to multiplexing the 998 975 * user may override. 999 976 * @runtime: A special argument for the parser only known at runtime. 977 + * @user_requested_cpu_list: Command line specified CPUs to record on. 978 + * @system_wide: Are events for all processes recorded. 1000 979 * @root_metric: Metrics may reference other metrics to form a tree. In this 1001 980 * case the root_metric holds all the IDs and a list of referenced 1002 981 * metrics. When adding a root this argument is NULL. ··· 1012 987 const char *modifier, 1013 988 bool metric_no_group, 1014 989 int runtime, 990 + const char *user_requested_cpu_list, 991 + bool system_wide, 1015 992 struct metric *root_metric, 1016 993 const struct visited_metric *visited, 1017 994 const struct pmu_events_table *table) ··· 1038 1011 * This metric is the root of a tree and may reference other 1039 1012 * metrics that are added recursively. 1040 1013 */ 1041 - root_metric = metric__new(pe, modifier, metric_no_group, runtime); 1014 + root_metric = metric__new(pe, modifier, metric_no_group, runtime, 1015 + user_requested_cpu_list, system_wide); 1042 1016 if (!root_metric) 1043 1017 return -ENOMEM; 1044 1018 ··· 1088 1060 ret = -EINVAL; 1089 1061 } else { 1090 1062 /* Resolve referenced metrics. */ 1091 - ret = resolve_metric(metric_list, modifier, metric_no_group, root_metric, 1092 - &visited_node, table); 1063 + ret = resolve_metric(metric_list, modifier, metric_no_group, 1064 + user_requested_cpu_list, system_wide, 1065 + root_metric, &visited_node, table); 1093 1066 } 1094 1067 1095 1068 if (ret) { ··· 1138 1109 const struct pmu_event *pe, 1139 1110 const char *modifier, 1140 1111 bool metric_no_group, 1112 + const char *user_requested_cpu_list, 1113 + bool system_wide, 1141 1114 struct metric *root_metric, 1142 1115 const struct visited_metric *visited, 1143 1116 const struct pmu_events_table *table) ··· 1150 1119 1151 1120 if (!strstr(pe->metric_expr, "?")) { 1152 1121 ret = __add_metric(metric_list, pe, modifier, metric_no_group, 0, 1153 - root_metric, visited, table); 1122 + user_requested_cpu_list, system_wide, root_metric, 1123 + visited, table); 1154 1124 } else { 1155 1125 int j, count; 1156 1126 ··· 1164 1132 1165 1133 for (j = 0; j < count && !ret; j++) 1166 1134 ret = __add_metric(metric_list, pe, modifier, metric_no_group, j, 1167 - root_metric, visited, table); 1135 + user_requested_cpu_list, system_wide, 1136 + root_metric, visited, table); 1168 1137 } 1169 1138 1170 1139 return ret; ··· 1182 1149 return 0; 1183 1150 1184 1151 ret = add_metric(d->metric_list, pe, d->modifier, d->metric_no_group, 1152 + d->user_requested_cpu_list, d->system_wide, 1185 1153 d->root_metric, d->visited, d->table); 1186 1154 if (ret) 1187 1155 goto out; ··· 1225 1191 struct list_head *list; 1226 1192 const char *metric_name; 1227 1193 const char *modifier; 1194 + const char *user_requested_cpu_list; 1228 1195 bool metric_no_group; 1196 + bool system_wide; 1229 1197 bool has_match; 1230 1198 }; 1231 1199 ··· 1244 1208 1245 1209 data->has_match = true; 1246 1210 ret = add_metric(data->list, pe, data->modifier, data->metric_no_group, 1247 - /*root_metric=*/NULL, 1248 - /*visited_metrics=*/NULL, table); 1211 + data->user_requested_cpu_list, data->system_wide, 1212 + /*root_metric=*/NULL, /*visited_metrics=*/NULL, table); 1249 1213 } 1250 1214 return ret; 1251 1215 } ··· 1259 1223 * @metric_no_group: Should events written to events be grouped "{}" or 1260 1224 * global. Grouping is the default but due to multiplexing the 1261 1225 * user may override. 1226 + * @user_requested_cpu_list: Command line specified CPUs to record on. 1227 + * @system_wide: Are events for all processes recorded. 1262 1228 * @metric_list: The list that the metric or metric group are added to. 1263 1229 * @table: The table that is searched for metrics, most commonly the table for the 1264 1230 * architecture perf is running upon. 1265 1231 */ 1266 1232 static int metricgroup__add_metric(const char *metric_name, const char *modifier, 1267 1233 bool metric_no_group, 1234 + const char *user_requested_cpu_list, 1235 + bool system_wide, 1268 1236 struct list_head *metric_list, 1269 1237 const struct pmu_events_table *table) 1270 1238 { ··· 1282 1242 .metric_name = metric_name, 1283 1243 .modifier = modifier, 1284 1244 .metric_no_group = metric_no_group, 1245 + .user_requested_cpu_list = user_requested_cpu_list, 1246 + .system_wide = system_wide, 1285 1247 .has_match = false, 1286 1248 }; 1287 1249 /* ··· 1305 1263 .metric_name = metric_name, 1306 1264 .modifier = modifier, 1307 1265 .metric_no_group = metric_no_group, 1266 + .user_requested_cpu_list = user_requested_cpu_list, 1267 + .system_wide = system_wide, 1308 1268 .has_match = &has_match, 1309 1269 .ret = &ret, 1310 1270 .table = table, ··· 1337 1293 * @metric_no_group: Should events written to events be grouped "{}" or 1338 1294 * global. Grouping is the default but due to multiplexing the 1339 1295 * user may override. 1296 + * @user_requested_cpu_list: Command line specified CPUs to record on. 1297 + * @system_wide: Are events for all processes recorded. 1340 1298 * @metric_list: The list that metrics are added to. 1341 1299 * @table: The table that is searched for metrics, most commonly the table for the 1342 1300 * architecture perf is running upon. 1343 1301 */ 1344 1302 static int metricgroup__add_metric_list(const char *list, bool metric_no_group, 1345 - struct list_head *metric_list, 1303 + const char *user_requested_cpu_list, 1304 + bool system_wide, struct list_head *metric_list, 1346 1305 const struct pmu_events_table *table) 1347 1306 { 1348 1307 char *list_itr, *list_copy, *metric_name, *modifier; ··· 1362 1315 *modifier++ = '\0'; 1363 1316 1364 1317 ret = metricgroup__add_metric(metric_name, modifier, 1365 - metric_no_group, metric_list, 1366 - table); 1318 + metric_no_group, user_requested_cpu_list, 1319 + system_wide, metric_list, table); 1367 1320 if (ret == -EINVAL) 1368 1321 pr_err("Cannot find metric or group `%s'\n", metric_name); 1369 1322 ··· 1552 1505 static int parse_groups(struct evlist *perf_evlist, const char *str, 1553 1506 bool metric_no_group, 1554 1507 bool metric_no_merge, 1508 + const char *user_requested_cpu_list, 1509 + bool system_wide, 1555 1510 struct perf_pmu *fake_pmu, 1556 1511 struct rblist *metric_events_list, 1557 1512 const struct pmu_events_table *table) ··· 1567 1518 if (metric_events_list->nr_entries == 0) 1568 1519 metricgroup__rblist_init(metric_events_list); 1569 1520 ret = metricgroup__add_metric_list(str, metric_no_group, 1570 - &metric_list, table); 1521 + user_requested_cpu_list, 1522 + system_wide, &metric_list, table); 1571 1523 if (ret) 1572 1524 goto out; 1573 1525 ··· 1700 1650 const char *str, 1701 1651 bool metric_no_group, 1702 1652 bool metric_no_merge, 1653 + const char *user_requested_cpu_list, 1654 + bool system_wide, 1703 1655 struct rblist *metric_events) 1704 1656 { 1705 1657 const struct pmu_events_table *table = pmu_events_table__find(); ··· 1709 1657 if (!table) 1710 1658 return -EINVAL; 1711 1659 1712 - return parse_groups(perf_evlist, str, metric_no_group, 1713 - metric_no_merge, NULL, metric_events, table); 1660 + return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge, 1661 + user_requested_cpu_list, system_wide, 1662 + /*fake_pmu=*/NULL, metric_events, table); 1714 1663 } 1715 1664 1716 1665 int metricgroup__parse_groups_test(struct evlist *evlist, ··· 1721 1668 bool metric_no_merge, 1722 1669 struct rblist *metric_events) 1723 1670 { 1724 - return parse_groups(evlist, str, metric_no_group, 1725 - metric_no_merge, &perf_pmu__fake, metric_events, table); 1671 + return parse_groups(evlist, str, metric_no_group, metric_no_merge, 1672 + /*user_requested_cpu_list=*/NULL, 1673 + /*system_wide=*/false, 1674 + &perf_pmu__fake, metric_events, table); 1726 1675 } 1727 1676 1728 1677 static int metricgroup__has_metric_callback(const struct pmu_event *pe,
+2
tools/perf/util/metricgroup.h
··· 68 68 const char *str, 69 69 bool metric_no_group, 70 70 bool metric_no_merge, 71 + const char *user_requested_cpu_list, 72 + bool system_wide, 71 73 struct rblist *metric_events); 72 74 int metricgroup__parse_groups_test(struct evlist *evlist, 73 75 const struct pmu_events_table *table,
+8 -3
tools/perf/util/stat-shadow.c
··· 911 911 if (!pctx) 912 912 return; 913 913 914 + if (config->user_requested_cpu_list) 915 + pctx->sctx.user_requested_cpu_list = strdup(config->user_requested_cpu_list); 914 916 pctx->sctx.runtime = runtime; 917 + pctx->sctx.system_wide = config->system_wide; 915 918 i = prepare_metric(metric_events, metric_refs, pctx, cpu_map_idx, st); 916 919 if (i < 0) { 917 920 expr__ctx_free(pctx); ··· 1307 1304 core_bound * 100.); 1308 1305 } else if (evsel->metric_expr) { 1309 1306 generic_metric(config, evsel->metric_expr, evsel->metric_events, NULL, 1310 - evsel->name, evsel->metric_name, NULL, 1, cpu_map_idx, out, st); 1307 + evsel->name, evsel->metric_name, NULL, 1, 1308 + cpu_map_idx, out, st); 1311 1309 } else if (runtime_stat_n(st, STAT_NSECS, cpu_map_idx, &rsd) != 0) { 1312 1310 char unit = ' '; 1313 1311 char unit_buf[10] = "/sec"; ··· 1333 1329 if (num++ > 0) 1334 1330 out->new_line(config, ctxp); 1335 1331 generic_metric(config, mexp->metric_expr, mexp->metric_events, 1336 - mexp->metric_refs, evsel->name, mexp->metric_name, 1337 - mexp->metric_unit, mexp->runtime, cpu_map_idx, out, st); 1332 + mexp->metric_refs, evsel->name, mexp->metric_name, 1333 + mexp->metric_unit, mexp->runtime, 1334 + cpu_map_idx, out, st); 1338 1335 } 1339 1336 } 1340 1337 if (num == 0)
+2
tools/perf/util/stat.h
··· 141 141 bool stop_read_counter; 142 142 bool quiet; 143 143 bool iostat_run; 144 + char *user_requested_cpu_list; 145 + bool system_wide; 144 146 FILE *output; 145 147 unsigned int interval; 146 148 unsigned int timeout;