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

perf header: Record non-CPU PMU capabilities

PMUs advertise their capabilities via sysfs attribute files but
the perf tool currently parses only core(CPU) or hybrid core PMU
capabilities. Add support of recording non-core PMU capabilities
int perf.data header.

Note that a newly proposed HEADER_PMU_CAPS is replacing existing
HEADER_HYBRID_CPU_PMU_CAPS. Special care is taken for hybrid core
PMUs by writing their capabilities first in the perf.data header
to make sure new perf.data file being read by old perf tool does
not break.

Reviewed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Ravi Bangoria <ravi.bangoria@amd.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Ananth Narayan <ananth.narayan@amd.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kim Phillips <kim.phillips@amd.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Robert Richter <rrichter@amd.com>
Cc: Sandipan Das <sandipan.das@amd.com>
Cc: Santosh Shukla <santosh.shukla@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: like.xu.linux@gmail.com
Cc: x86@kernel.org
Link: https://lore.kernel.org/r/20220604044519.594-6-ravi.bangoria@amd.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ravi Bangoria and committed by
Arnaldo Carvalho de Melo
2139f742 ff34eaa8

+158 -88
+6 -4
tools/perf/Documentation/perf.data-file-format.txt
··· 419 419 cpu_core cpu list : 0-15 420 420 cpu_atom cpu list : 16-23 421 421 422 - HEADER_HYBRID_CPU_PMU_CAPS = 31, 422 + HEADER_PMU_CAPS = 31, 423 423 424 - A list of hybrid CPU PMU capabilities. 424 + List of pmu capabilities (except cpu pmu which is already 425 + covered by HEADER_CPU_PMU_CAPS). Note that hybrid cpu pmu 426 + capabilities are also stored here. 425 427 426 428 struct { 427 429 u32 nr_pmu; 428 430 struct { 429 - u32 nr_cpu_pmu_caps; 431 + u32 nr_caps; 430 432 { 431 433 char name[]; 432 434 char value[]; 433 - } [nr_cpu_pmu_caps]; 435 + } [nr_caps]; 434 436 char pmu_name[]; 435 437 } [nr_pmu]; 436 438 };
+1 -1
tools/perf/builtin-inject.c
··· 809 809 case HEADER_CPU_PMU_CAPS: 810 810 case HEADER_CLOCK_DATA: 811 811 case HEADER_HYBRID_TOPOLOGY: 812 - case HEADER_HYBRID_CPU_PMU_CAPS: 812 + case HEADER_PMU_CAPS: 813 813 return true; 814 814 /* Information that can be updated */ 815 815 case HEADER_BUILD_ID:
+54 -6
tools/perf/util/env.c
··· 219 219 } 220 220 zfree(&env->hybrid_nodes); 221 221 222 - for (i = 0; i < env->nr_hybrid_cpc_nodes; i++) { 223 - for (j = 0; j < env->hybrid_cpc_nodes[i].nr_cpu_pmu_caps; j++) 224 - zfree(&env->hybrid_cpc_nodes[i].cpu_pmu_caps[j]); 225 - zfree(&env->hybrid_cpc_nodes[i].cpu_pmu_caps); 226 - zfree(&env->hybrid_cpc_nodes[i].pmu_name); 222 + for (i = 0; i < env->nr_pmus_with_caps; i++) { 223 + for (j = 0; j < env->pmu_caps[i].nr_caps; j++) 224 + zfree(&env->pmu_caps[i].caps[j]); 225 + zfree(&env->pmu_caps[i].caps); 226 + zfree(&env->pmu_caps[i].pmu_name); 227 227 } 228 - zfree(&env->hybrid_cpc_nodes); 228 + zfree(&env->pmu_caps); 229 229 } 230 230 231 231 void perf_env__init(struct perf_env *env) ··· 530 530 } 531 531 532 532 return cpu.cpu >= 0 && cpu.cpu < env->nr_numa_map ? env->numa_map[cpu.cpu] : -1; 533 + } 534 + 535 + char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name, 536 + const char *cap) 537 + { 538 + char *cap_eq; 539 + int cap_size; 540 + char **ptr; 541 + int i, j; 542 + 543 + if (!pmu_name || !cap) 544 + return NULL; 545 + 546 + cap_size = strlen(cap); 547 + cap_eq = zalloc(cap_size + 2); 548 + if (!cap_eq) 549 + return NULL; 550 + 551 + memcpy(cap_eq, cap, cap_size); 552 + cap_eq[cap_size] = '='; 553 + 554 + if (!strcmp(pmu_name, "cpu")) { 555 + for (i = 0; i < env->nr_cpu_pmu_caps; i++) { 556 + if (!strncmp(env->cpu_pmu_caps[i], cap_eq, cap_size + 1)) { 557 + free(cap_eq); 558 + return &env->cpu_pmu_caps[i][cap_size + 1]; 559 + } 560 + } 561 + goto out; 562 + } 563 + 564 + for (i = 0; i < env->nr_pmus_with_caps; i++) { 565 + if (strcmp(env->pmu_caps[i].pmu_name, pmu_name)) 566 + continue; 567 + 568 + ptr = env->pmu_caps[i].caps; 569 + 570 + for (j = 0; j < env->pmu_caps[i].nr_caps; j++) { 571 + if (!strncmp(ptr[j], cap_eq, cap_size + 1)) { 572 + free(cap_eq); 573 + return &ptr[j][cap_size + 1]; 574 + } 575 + } 576 + } 577 + 578 + out: 579 + free(cap_eq); 580 + return NULL; 533 581 }
+7 -5
tools/perf/util/env.h
··· 43 43 char *cpus; 44 44 }; 45 45 46 - struct hybrid_cpc_node { 47 - int nr_cpu_pmu_caps; 46 + struct pmu_caps { 47 + int nr_caps; 48 48 unsigned int max_branches; 49 - char **cpu_pmu_caps; 49 + char **caps; 50 50 char *pmu_name; 51 51 }; 52 52 ··· 74 74 int nr_groups; 75 75 int nr_cpu_pmu_caps; 76 76 int nr_hybrid_nodes; 77 - int nr_hybrid_cpc_nodes; 77 + int nr_pmus_with_caps; 78 78 char *cmdline; 79 79 const char **cmdline_argv; 80 80 char *sibling_cores; ··· 94 94 struct memory_node *memory_nodes; 95 95 unsigned long long memory_bsize; 96 96 struct hybrid_node *hybrid_nodes; 97 - struct hybrid_cpc_node *hybrid_cpc_nodes; 97 + struct pmu_caps *pmu_caps; 98 98 #ifdef HAVE_LIBBPF_SUPPORT 99 99 /* 100 100 * bpf_info_lock protects bpf rbtrees. This is needed because the ··· 172 172 struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id); 173 173 174 174 int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu); 175 + char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name, 176 + const char *cap); 175 177 #endif /* __PERF_ENV_H */
+89 -71
tools/perf/util/header.c
··· 1512 1512 return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len)); 1513 1513 } 1514 1514 1515 - static int write_per_cpu_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu, 1516 - bool write_pmu) 1515 + static int __write_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu, 1516 + bool write_pmu) 1517 1517 { 1518 1518 struct perf_pmu_caps *caps = NULL; 1519 - int nr_caps; 1520 1519 int ret; 1521 1520 1522 - nr_caps = perf_pmu__caps_parse(pmu); 1523 - if (nr_caps < 0) 1524 - return nr_caps; 1525 - 1526 - ret = do_write(ff, &nr_caps, sizeof(nr_caps)); 1521 + ret = do_write(ff, &pmu->nr_caps, sizeof(pmu->nr_caps)); 1527 1522 if (ret < 0) 1528 1523 return ret; 1529 1524 ··· 1545 1550 struct evlist *evlist __maybe_unused) 1546 1551 { 1547 1552 struct perf_pmu *cpu_pmu = perf_pmu__find("cpu"); 1553 + int ret; 1548 1554 1549 1555 if (!cpu_pmu) 1550 1556 return -ENOENT; 1551 1557 1552 - return write_per_cpu_pmu_caps(ff, cpu_pmu, false); 1558 + ret = perf_pmu__caps_parse(cpu_pmu); 1559 + if (ret < 0) 1560 + return ret; 1561 + 1562 + return __write_pmu_caps(ff, cpu_pmu, false); 1553 1563 } 1554 1564 1555 - static int write_hybrid_cpu_pmu_caps(struct feat_fd *ff, 1556 - struct evlist *evlist __maybe_unused) 1565 + static int write_pmu_caps(struct feat_fd *ff, 1566 + struct evlist *evlist __maybe_unused) 1557 1567 { 1558 - struct perf_pmu *pmu; 1559 - u32 nr_pmu = perf_pmu__hybrid_pmu_num(); 1568 + struct perf_pmu *pmu = NULL; 1569 + int nr_pmu = 0; 1560 1570 int ret; 1561 1571 1562 - if (nr_pmu == 0) 1563 - return -ENOENT; 1572 + while ((pmu = perf_pmu__scan(pmu))) { 1573 + if (!pmu->name || !strcmp(pmu->name, "cpu") || 1574 + perf_pmu__caps_parse(pmu) <= 0) 1575 + continue; 1576 + nr_pmu++; 1577 + } 1564 1578 1565 1579 ret = do_write(ff, &nr_pmu, sizeof(nr_pmu)); 1566 1580 if (ret < 0) 1567 1581 return ret; 1568 1582 1583 + if (!nr_pmu) 1584 + return 0; 1585 + 1586 + /* 1587 + * Write hybrid pmu caps first to maintain compatibility with 1588 + * older perf tool. 1589 + */ 1590 + pmu = NULL; 1569 1591 perf_pmu__for_each_hybrid_pmu(pmu) { 1570 - ret = write_per_cpu_pmu_caps(ff, pmu, true); 1592 + ret = __write_pmu_caps(ff, pmu, true); 1571 1593 if (ret < 0) 1572 1594 return ret; 1573 1595 } 1574 1596 1597 + pmu = NULL; 1598 + while ((pmu = perf_pmu__scan(pmu))) { 1599 + if (!pmu->name || !strcmp(pmu->name, "cpu") || 1600 + !pmu->nr_caps || perf_pmu__is_hybrid(pmu->name)) 1601 + continue; 1602 + 1603 + ret = __write_pmu_caps(ff, pmu, true); 1604 + if (ret < 0) 1605 + return ret; 1606 + } 1575 1607 return 0; 1576 1608 } 1577 1609 ··· 2073 2051 ff->ph->env.comp_level, ff->ph->env.comp_ratio); 2074 2052 } 2075 2053 2076 - static void print_per_cpu_pmu_caps(FILE *fp, int nr_caps, char **cpu_pmu_caps, 2077 - char *pmu_name) 2054 + static void __print_pmu_caps(FILE *fp, int nr_caps, char **caps, char *pmu_name) 2078 2055 { 2079 2056 const char *delimiter = ""; 2080 2057 int i; ··· 2085 2064 2086 2065 fprintf(fp, "# %s pmu capabilities: ", pmu_name); 2087 2066 for (i = 0; i < nr_caps; i++) { 2088 - fprintf(fp, "%s%s", delimiter, cpu_pmu_caps[i]); 2067 + fprintf(fp, "%s%s", delimiter, caps[i]); 2089 2068 delimiter = ", "; 2090 2069 } 2091 2070 ··· 2094 2073 2095 2074 static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) 2096 2075 { 2097 - print_per_cpu_pmu_caps(fp, ff->ph->env.nr_cpu_pmu_caps, 2098 - ff->ph->env.cpu_pmu_caps, (char *)"cpu"); 2076 + __print_pmu_caps(fp, ff->ph->env.nr_cpu_pmu_caps, 2077 + ff->ph->env.cpu_pmu_caps, (char *)"cpu"); 2099 2078 } 2100 2079 2101 - static void print_hybrid_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) 2080 + static void print_pmu_caps(struct feat_fd *ff, FILE *fp) 2102 2081 { 2103 - struct hybrid_cpc_node *n; 2082 + struct pmu_caps *pmu_caps; 2104 2083 2105 - for (int i = 0; i < ff->ph->env.nr_hybrid_cpc_nodes; i++) { 2106 - n = &ff->ph->env.hybrid_cpc_nodes[i]; 2107 - print_per_cpu_pmu_caps(fp, n->nr_cpu_pmu_caps, 2108 - n->cpu_pmu_caps, 2109 - n->pmu_name); 2084 + for (int i = 0; i < ff->ph->env.nr_pmus_with_caps; i++) { 2085 + pmu_caps = &ff->ph->env.pmu_caps[i]; 2086 + __print_pmu_caps(fp, pmu_caps->nr_caps, pmu_caps->caps, 2087 + pmu_caps->pmu_name); 2110 2088 } 2111 2089 } 2112 2090 ··· 3216 3196 return 0; 3217 3197 } 3218 3198 3219 - static int process_per_cpu_pmu_caps(struct feat_fd *ff, int *nr_cpu_pmu_caps, 3220 - char ***cpu_pmu_caps, 3221 - unsigned int *max_branches) 3199 + static int __process_pmu_caps(struct feat_fd *ff, int *nr_caps, 3200 + char ***caps, unsigned int *max_branches) 3222 3201 { 3223 3202 char *name, *value, *ptr; 3224 - u32 nr_caps, i; 3203 + u32 nr_pmu_caps, i; 3225 3204 3226 - *nr_cpu_pmu_caps = 0; 3227 - *cpu_pmu_caps = NULL; 3205 + *nr_caps = 0; 3206 + *caps = NULL; 3228 3207 3229 - if (do_read_u32(ff, &nr_caps)) 3208 + if (do_read_u32(ff, &nr_pmu_caps)) 3230 3209 return -1; 3231 3210 3232 - if (!nr_caps) 3211 + if (!nr_pmu_caps) 3233 3212 return 0; 3234 3213 3235 - *cpu_pmu_caps = zalloc(sizeof(char *) * nr_caps); 3236 - if (!*cpu_pmu_caps) 3214 + *caps = zalloc(sizeof(char *) * nr_pmu_caps); 3215 + if (!*caps) 3237 3216 return -1; 3238 3217 3239 - for (i = 0; i < nr_caps; i++) { 3218 + for (i = 0; i < nr_pmu_caps; i++) { 3240 3219 name = do_read_string(ff); 3241 3220 if (!name) 3242 3221 goto error; ··· 3247 3228 if (asprintf(&ptr, "%s=%s", name, value) < 0) 3248 3229 goto free_value; 3249 3230 3250 - (*cpu_pmu_caps)[i] = ptr; 3231 + (*caps)[i] = ptr; 3251 3232 3252 3233 if (!strcmp(name, "branches")) 3253 3234 *max_branches = atoi(value); ··· 3255 3236 free(value); 3256 3237 free(name); 3257 3238 } 3258 - *nr_cpu_pmu_caps = nr_caps; 3239 + *nr_caps = nr_pmu_caps; 3259 3240 return 0; 3260 3241 3261 3242 free_value: ··· 3264 3245 free(name); 3265 3246 error: 3266 3247 for (; i > 0; i--) 3267 - free((*cpu_pmu_caps)[i - 1]); 3268 - free(*cpu_pmu_caps); 3269 - *cpu_pmu_caps = NULL; 3270 - *nr_cpu_pmu_caps = 0; 3248 + free((*caps)[i - 1]); 3249 + free(*caps); 3250 + *caps = NULL; 3251 + *nr_caps = 0; 3271 3252 return -1; 3272 3253 } 3273 3254 3274 3255 static int process_cpu_pmu_caps(struct feat_fd *ff, 3275 3256 void *data __maybe_unused) 3276 3257 { 3277 - int ret = process_per_cpu_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps, 3278 - &ff->ph->env.cpu_pmu_caps, 3279 - &ff->ph->env.max_branches); 3258 + int ret = __process_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps, 3259 + &ff->ph->env.cpu_pmu_caps, 3260 + &ff->ph->env.max_branches); 3280 3261 3281 3262 if (!ret && !ff->ph->env.cpu_pmu_caps) 3282 3263 pr_debug("cpu pmu capabilities not available\n"); 3283 3264 return ret; 3284 3265 } 3285 3266 3286 - static int process_hybrid_cpu_pmu_caps(struct feat_fd *ff, 3287 - void *data __maybe_unused) 3267 + static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused) 3288 3268 { 3289 - struct hybrid_cpc_node *nodes; 3269 + struct pmu_caps *pmu_caps; 3290 3270 u32 nr_pmu, i; 3291 3271 int ret; 3292 3272 int j; ··· 3294 3276 return -1; 3295 3277 3296 3278 if (!nr_pmu) { 3297 - pr_debug("hybrid cpu pmu capabilities not available\n"); 3279 + pr_debug("pmu capabilities not available\n"); 3298 3280 return 0; 3299 3281 } 3300 3282 3301 - nodes = zalloc(sizeof(*nodes) * nr_pmu); 3302 - if (!nodes) 3283 + pmu_caps = zalloc(sizeof(*pmu_caps) * nr_pmu); 3284 + if (!pmu_caps) 3303 3285 return -ENOMEM; 3304 3286 3305 3287 for (i = 0; i < nr_pmu; i++) { 3306 - struct hybrid_cpc_node *n = &nodes[i]; 3307 - 3308 - ret = process_per_cpu_pmu_caps(ff, &n->nr_cpu_pmu_caps, 3309 - &n->cpu_pmu_caps, 3310 - &n->max_branches); 3288 + ret = __process_pmu_caps(ff, &pmu_caps[i].nr_caps, 3289 + &pmu_caps[i].caps, 3290 + &pmu_caps[i].max_branches); 3311 3291 if (ret) 3312 3292 goto err; 3313 3293 3314 - n->pmu_name = do_read_string(ff); 3315 - if (!n->pmu_name) { 3294 + pmu_caps[i].pmu_name = do_read_string(ff); 3295 + if (!pmu_caps[i].pmu_name) { 3316 3296 ret = -1; 3317 3297 goto err; 3318 3298 } 3319 - if (!n->nr_cpu_pmu_caps) 3320 - pr_debug("%s pmu capabilities not available\n", n->pmu_name); 3299 + if (!pmu_caps[i].nr_caps) { 3300 + pr_debug("%s pmu capabilities not available\n", 3301 + pmu_caps[i].pmu_name); 3302 + } 3321 3303 } 3322 3304 3323 - ff->ph->env.nr_hybrid_cpc_nodes = nr_pmu; 3324 - ff->ph->env.hybrid_cpc_nodes = nodes; 3305 + ff->ph->env.nr_pmus_with_caps = nr_pmu; 3306 + ff->ph->env.pmu_caps = pmu_caps; 3325 3307 return 0; 3326 3308 3327 3309 err: 3328 3310 for (i = 0; i < nr_pmu; i++) { 3329 - for (j = 0; j < nodes[i].nr_cpu_pmu_caps; j++) 3330 - free(nodes[i].cpu_pmu_caps[j]); 3331 - free(nodes[i].cpu_pmu_caps); 3332 - free(nodes[i].pmu_name); 3311 + for (j = 0; j < pmu_caps[i].nr_caps; j++) 3312 + free(pmu_caps[i].caps[j]); 3313 + free(pmu_caps[i].caps); 3314 + free(pmu_caps[i].pmu_name); 3333 3315 } 3334 3316 3335 - free(nodes); 3317 + free(pmu_caps); 3336 3318 return ret; 3337 3319 } 3338 3320 ··· 3398 3380 FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false), 3399 3381 FEAT_OPR(CLOCK_DATA, clock_data, false), 3400 3382 FEAT_OPN(HYBRID_TOPOLOGY, hybrid_topology, true), 3401 - FEAT_OPR(HYBRID_CPU_PMU_CAPS, hybrid_cpu_pmu_caps, false), 3383 + FEAT_OPR(PMU_CAPS, pmu_caps, false), 3402 3384 }; 3403 3385 3404 3386 struct header_print_data {
+1 -1
tools/perf/util/header.h
··· 46 46 HEADER_CPU_PMU_CAPS, 47 47 HEADER_CLOCK_DATA, 48 48 HEADER_HYBRID_TOPOLOGY, 49 - HEADER_HYBRID_CPU_PMU_CAPS, 49 + HEADER_PMU_CAPS, 50 50 HEADER_LAST_FEATURE, 51 51 HEADER_FEAT_BITS = 256, 52 52 };