1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include "cpumap.h" |
3 | #include "debug.h" |
4 | #include "env.h" |
5 | #include "util/header.h" |
6 | #include "linux/compiler.h" |
7 | #include <linux/ctype.h> |
8 | #include <linux/zalloc.h> |
9 | #include "cgroup.h" |
10 | #include <errno.h> |
11 | #include <sys/utsname.h> |
12 | #include <stdlib.h> |
13 | #include <string.h> |
14 | #include "pmus.h" |
15 | #include "strbuf.h" |
16 | #include "trace/beauty/beauty.h" |
17 | |
18 | struct perf_env perf_env; |
19 | |
20 | #ifdef HAVE_LIBBPF_SUPPORT |
21 | #include "bpf-event.h" |
22 | #include "bpf-utils.h" |
23 | #include <bpf/libbpf.h> |
24 | |
25 | void perf_env__insert_bpf_prog_info(struct perf_env *env, |
26 | struct bpf_prog_info_node *info_node) |
27 | { |
28 | down_write(&env->bpf_progs.lock); |
29 | __perf_env__insert_bpf_prog_info(env, info_node); |
30 | up_write(&env->bpf_progs.lock); |
31 | } |
32 | |
33 | void __perf_env__insert_bpf_prog_info(struct perf_env *env, struct bpf_prog_info_node *info_node) |
34 | { |
35 | __u32 prog_id = info_node->info_linear->info.id; |
36 | struct bpf_prog_info_node *node; |
37 | struct rb_node *parent = NULL; |
38 | struct rb_node **p; |
39 | |
40 | p = &env->bpf_progs.infos.rb_node; |
41 | |
42 | while (*p != NULL) { |
43 | parent = *p; |
44 | node = rb_entry(parent, struct bpf_prog_info_node, rb_node); |
45 | if (prog_id < node->info_linear->info.id) { |
46 | p = &(*p)->rb_left; |
47 | } else if (prog_id > node->info_linear->info.id) { |
48 | p = &(*p)->rb_right; |
49 | } else { |
50 | pr_debug("duplicated bpf prog info %u\n" , prog_id); |
51 | return; |
52 | } |
53 | } |
54 | |
55 | rb_link_node(&info_node->rb_node, parent, p); |
56 | rb_insert_color(&info_node->rb_node, &env->bpf_progs.infos); |
57 | env->bpf_progs.infos_cnt++; |
58 | } |
59 | |
60 | struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env, |
61 | __u32 prog_id) |
62 | { |
63 | struct bpf_prog_info_node *node = NULL; |
64 | struct rb_node *n; |
65 | |
66 | down_read(&env->bpf_progs.lock); |
67 | n = env->bpf_progs.infos.rb_node; |
68 | |
69 | while (n) { |
70 | node = rb_entry(n, struct bpf_prog_info_node, rb_node); |
71 | if (prog_id < node->info_linear->info.id) |
72 | n = n->rb_left; |
73 | else if (prog_id > node->info_linear->info.id) |
74 | n = n->rb_right; |
75 | else |
76 | goto out; |
77 | } |
78 | node = NULL; |
79 | |
80 | out: |
81 | up_read(&env->bpf_progs.lock); |
82 | return node; |
83 | } |
84 | |
85 | bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node) |
86 | { |
87 | bool ret; |
88 | |
89 | down_write(&env->bpf_progs.lock); |
90 | ret = __perf_env__insert_btf(env, btf_node); |
91 | up_write(&env->bpf_progs.lock); |
92 | return ret; |
93 | } |
94 | |
95 | bool __perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node) |
96 | { |
97 | struct rb_node *parent = NULL; |
98 | __u32 btf_id = btf_node->id; |
99 | struct btf_node *node; |
100 | struct rb_node **p; |
101 | |
102 | p = &env->bpf_progs.btfs.rb_node; |
103 | |
104 | while (*p != NULL) { |
105 | parent = *p; |
106 | node = rb_entry(parent, struct btf_node, rb_node); |
107 | if (btf_id < node->id) { |
108 | p = &(*p)->rb_left; |
109 | } else if (btf_id > node->id) { |
110 | p = &(*p)->rb_right; |
111 | } else { |
112 | pr_debug("duplicated btf %u\n" , btf_id); |
113 | return false; |
114 | } |
115 | } |
116 | |
117 | rb_link_node(&btf_node->rb_node, parent, p); |
118 | rb_insert_color(&btf_node->rb_node, &env->bpf_progs.btfs); |
119 | env->bpf_progs.btfs_cnt++; |
120 | return true; |
121 | } |
122 | |
123 | struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id) |
124 | { |
125 | struct btf_node *res; |
126 | |
127 | down_read(&env->bpf_progs.lock); |
128 | res = __perf_env__find_btf(env, btf_id); |
129 | up_read(&env->bpf_progs.lock); |
130 | return res; |
131 | } |
132 | |
133 | struct btf_node *__perf_env__find_btf(struct perf_env *env, __u32 btf_id) |
134 | { |
135 | struct btf_node *node = NULL; |
136 | struct rb_node *n; |
137 | |
138 | n = env->bpf_progs.btfs.rb_node; |
139 | |
140 | while (n) { |
141 | node = rb_entry(n, struct btf_node, rb_node); |
142 | if (btf_id < node->id) |
143 | n = n->rb_left; |
144 | else if (btf_id > node->id) |
145 | n = n->rb_right; |
146 | else |
147 | return node; |
148 | } |
149 | return NULL; |
150 | } |
151 | |
152 | /* purge data in bpf_progs.infos tree */ |
153 | static void perf_env__purge_bpf(struct perf_env *env) |
154 | { |
155 | struct rb_root *root; |
156 | struct rb_node *next; |
157 | |
158 | down_write(&env->bpf_progs.lock); |
159 | |
160 | root = &env->bpf_progs.infos; |
161 | next = rb_first(root); |
162 | |
163 | while (next) { |
164 | struct bpf_prog_info_node *node; |
165 | |
166 | node = rb_entry(next, struct bpf_prog_info_node, rb_node); |
167 | next = rb_next(&node->rb_node); |
168 | rb_erase(&node->rb_node, root); |
169 | zfree(&node->info_linear); |
170 | free(node); |
171 | } |
172 | |
173 | env->bpf_progs.infos_cnt = 0; |
174 | |
175 | root = &env->bpf_progs.btfs; |
176 | next = rb_first(root); |
177 | |
178 | while (next) { |
179 | struct btf_node *node; |
180 | |
181 | node = rb_entry(next, struct btf_node, rb_node); |
182 | next = rb_next(&node->rb_node); |
183 | rb_erase(&node->rb_node, root); |
184 | free(node); |
185 | } |
186 | |
187 | env->bpf_progs.btfs_cnt = 0; |
188 | |
189 | up_write(&env->bpf_progs.lock); |
190 | } |
191 | #else // HAVE_LIBBPF_SUPPORT |
192 | static void perf_env__purge_bpf(struct perf_env *env __maybe_unused) |
193 | { |
194 | } |
195 | #endif // HAVE_LIBBPF_SUPPORT |
196 | |
197 | void perf_env__exit(struct perf_env *env) |
198 | { |
199 | int i, j; |
200 | |
201 | perf_env__purge_bpf(env); |
202 | perf_env__purge_cgroups(env); |
203 | zfree(&env->hostname); |
204 | zfree(&env->os_release); |
205 | zfree(&env->version); |
206 | zfree(&env->arch); |
207 | zfree(&env->cpu_desc); |
208 | zfree(&env->cpuid); |
209 | zfree(&env->cmdline); |
210 | zfree(&env->cmdline_argv); |
211 | zfree(&env->sibling_dies); |
212 | zfree(&env->sibling_cores); |
213 | zfree(&env->sibling_threads); |
214 | zfree(&env->pmu_mappings); |
215 | zfree(&env->cpu); |
216 | for (i = 0; i < env->nr_cpu_pmu_caps; i++) |
217 | zfree(&env->cpu_pmu_caps[i]); |
218 | zfree(&env->cpu_pmu_caps); |
219 | zfree(&env->numa_map); |
220 | |
221 | for (i = 0; i < env->nr_numa_nodes; i++) |
222 | perf_cpu_map__put(env->numa_nodes[i].map); |
223 | zfree(&env->numa_nodes); |
224 | |
225 | for (i = 0; i < env->caches_cnt; i++) |
226 | cpu_cache_level__free(cache: &env->caches[i]); |
227 | zfree(&env->caches); |
228 | |
229 | for (i = 0; i < env->nr_memory_nodes; i++) |
230 | zfree(&env->memory_nodes[i].set); |
231 | zfree(&env->memory_nodes); |
232 | |
233 | for (i = 0; i < env->nr_hybrid_nodes; i++) { |
234 | zfree(&env->hybrid_nodes[i].pmu_name); |
235 | zfree(&env->hybrid_nodes[i].cpus); |
236 | } |
237 | zfree(&env->hybrid_nodes); |
238 | |
239 | for (i = 0; i < env->nr_pmus_with_caps; i++) { |
240 | for (j = 0; j < env->pmu_caps[i].nr_caps; j++) |
241 | zfree(&env->pmu_caps[i].caps[j]); |
242 | zfree(&env->pmu_caps[i].caps); |
243 | zfree(&env->pmu_caps[i].pmu_name); |
244 | } |
245 | zfree(&env->pmu_caps); |
246 | } |
247 | |
248 | void perf_env__init(struct perf_env *env) |
249 | { |
250 | #ifdef HAVE_LIBBPF_SUPPORT |
251 | env->bpf_progs.infos = RB_ROOT; |
252 | env->bpf_progs.btfs = RB_ROOT; |
253 | init_rwsem(&env->bpf_progs.lock); |
254 | #endif |
255 | env->kernel_is_64_bit = -1; |
256 | } |
257 | |
258 | static void perf_env__init_kernel_mode(struct perf_env *env) |
259 | { |
260 | const char *arch = perf_env__raw_arch(env); |
261 | |
262 | if (!strncmp(arch, "x86_64" , 6) || !strncmp(arch, "aarch64" , 7) || |
263 | !strncmp(arch, "arm64" , 5) || !strncmp(arch, "mips64" , 6) || |
264 | !strncmp(arch, "parisc64" , 8) || !strncmp(arch, "riscv64" , 7) || |
265 | !strncmp(arch, "s390x" , 5) || !strncmp(arch, "sparc64" , 7)) |
266 | env->kernel_is_64_bit = 1; |
267 | else |
268 | env->kernel_is_64_bit = 0; |
269 | } |
270 | |
271 | int perf_env__kernel_is_64_bit(struct perf_env *env) |
272 | { |
273 | if (env->kernel_is_64_bit == -1) |
274 | perf_env__init_kernel_mode(env); |
275 | |
276 | return env->kernel_is_64_bit; |
277 | } |
278 | |
279 | int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]) |
280 | { |
281 | int i; |
282 | |
283 | /* do not include NULL termination */ |
284 | env->cmdline_argv = calloc(argc, sizeof(char *)); |
285 | if (env->cmdline_argv == NULL) |
286 | goto out_enomem; |
287 | |
288 | /* |
289 | * Must copy argv contents because it gets moved around during option |
290 | * parsing: |
291 | */ |
292 | for (i = 0; i < argc ; i++) { |
293 | env->cmdline_argv[i] = argv[i]; |
294 | if (env->cmdline_argv[i] == NULL) |
295 | goto out_free; |
296 | } |
297 | |
298 | env->nr_cmdline = argc; |
299 | |
300 | return 0; |
301 | out_free: |
302 | zfree(&env->cmdline_argv); |
303 | out_enomem: |
304 | return -ENOMEM; |
305 | } |
306 | |
307 | int perf_env__read_cpu_topology_map(struct perf_env *env) |
308 | { |
309 | int idx, nr_cpus; |
310 | |
311 | if (env->cpu != NULL) |
312 | return 0; |
313 | |
314 | if (env->nr_cpus_avail == 0) |
315 | env->nr_cpus_avail = cpu__max_present_cpu().cpu; |
316 | |
317 | nr_cpus = env->nr_cpus_avail; |
318 | if (nr_cpus == -1) |
319 | return -EINVAL; |
320 | |
321 | env->cpu = calloc(nr_cpus, sizeof(env->cpu[0])); |
322 | if (env->cpu == NULL) |
323 | return -ENOMEM; |
324 | |
325 | for (idx = 0; idx < nr_cpus; ++idx) { |
326 | struct perf_cpu cpu = { .cpu = idx }; |
327 | |
328 | env->cpu[idx].core_id = cpu__get_core_id(cpu: cpu); |
329 | env->cpu[idx].socket_id = cpu__get_socket_id(cpu: cpu); |
330 | env->cpu[idx].die_id = cpu__get_die_id(cpu: cpu); |
331 | } |
332 | |
333 | env->nr_cpus_avail = nr_cpus; |
334 | return 0; |
335 | } |
336 | |
337 | int perf_env__read_pmu_mappings(struct perf_env *env) |
338 | { |
339 | struct perf_pmu *pmu = NULL; |
340 | u32 pmu_num = 0; |
341 | struct strbuf sb; |
342 | |
343 | while ((pmu = perf_pmus__scan(pmu))) |
344 | pmu_num++; |
345 | |
346 | if (!pmu_num) { |
347 | pr_debug("pmu mappings not available\n" ); |
348 | return -ENOENT; |
349 | } |
350 | env->nr_pmu_mappings = pmu_num; |
351 | |
352 | if (strbuf_init(buf: &sb, hint: 128 * pmu_num) < 0) |
353 | return -ENOMEM; |
354 | |
355 | while ((pmu = perf_pmus__scan(pmu))) { |
356 | if (strbuf_addf(sb: &sb, fmt: "%u:%s" , pmu->type, pmu->name) < 0) |
357 | goto error; |
358 | /* include a NULL character at the end */ |
359 | if (strbuf_add(buf: &sb, "" , 1) < 0) |
360 | goto error; |
361 | } |
362 | |
363 | env->pmu_mappings = strbuf_detach(buf: &sb, NULL); |
364 | |
365 | return 0; |
366 | |
367 | error: |
368 | strbuf_release(buf: &sb); |
369 | return -1; |
370 | } |
371 | |
372 | int perf_env__read_cpuid(struct perf_env *env) |
373 | { |
374 | char cpuid[128]; |
375 | int err = get_cpuid(cpuid, sizeof(cpuid)); |
376 | |
377 | if (err) |
378 | return err; |
379 | |
380 | free(env->cpuid); |
381 | env->cpuid = strdup(cpuid); |
382 | if (env->cpuid == NULL) |
383 | return ENOMEM; |
384 | return 0; |
385 | } |
386 | |
387 | static int perf_env__read_arch(struct perf_env *env) |
388 | { |
389 | struct utsname uts; |
390 | |
391 | if (env->arch) |
392 | return 0; |
393 | |
394 | if (!uname(&uts)) |
395 | env->arch = strdup(uts.machine); |
396 | |
397 | return env->arch ? 0 : -ENOMEM; |
398 | } |
399 | |
400 | static int perf_env__read_nr_cpus_avail(struct perf_env *env) |
401 | { |
402 | if (env->nr_cpus_avail == 0) |
403 | env->nr_cpus_avail = cpu__max_present_cpu().cpu; |
404 | |
405 | return env->nr_cpus_avail ? 0 : -ENOENT; |
406 | } |
407 | |
408 | const char *perf_env__raw_arch(struct perf_env *env) |
409 | { |
410 | return env && !perf_env__read_arch(env) ? env->arch : "unknown" ; |
411 | } |
412 | |
413 | int perf_env__nr_cpus_avail(struct perf_env *env) |
414 | { |
415 | return env && !perf_env__read_nr_cpus_avail(env) ? env->nr_cpus_avail : 0; |
416 | } |
417 | |
418 | void cpu_cache_level__free(struct cpu_cache_level *cache) |
419 | { |
420 | zfree(&cache->type); |
421 | zfree(&cache->map); |
422 | zfree(&cache->size); |
423 | } |
424 | |
425 | /* |
426 | * Return architecture name in a normalized form. |
427 | * The conversion logic comes from the Makefile. |
428 | */ |
429 | static const char *normalize_arch(char *arch) |
430 | { |
431 | if (!strcmp(arch, "x86_64" )) |
432 | return "x86" ; |
433 | if (arch[0] == 'i' && arch[2] == '8' && arch[3] == '6') |
434 | return "x86" ; |
435 | if (!strcmp(arch, "sun4u" ) || !strncmp(arch, "sparc" , 5)) |
436 | return "sparc" ; |
437 | if (!strncmp(arch, "aarch64" , 7) || !strncmp(arch, "arm64" , 5)) |
438 | return "arm64" ; |
439 | if (!strncmp(arch, "arm" , 3) || !strcmp(arch, "sa110" )) |
440 | return "arm" ; |
441 | if (!strncmp(arch, "s390" , 4)) |
442 | return "s390" ; |
443 | if (!strncmp(arch, "parisc" , 6)) |
444 | return "parisc" ; |
445 | if (!strncmp(arch, "powerpc" , 7) || !strncmp(arch, "ppc" , 3)) |
446 | return "powerpc" ; |
447 | if (!strncmp(arch, "mips" , 4)) |
448 | return "mips" ; |
449 | if (!strncmp(arch, "sh" , 2) && isdigit(c: arch[2])) |
450 | return "sh" ; |
451 | if (!strncmp(arch, "loongarch" , 9)) |
452 | return "loongarch" ; |
453 | |
454 | return arch; |
455 | } |
456 | |
457 | const char *perf_env__arch(struct perf_env *env) |
458 | { |
459 | char *arch_name; |
460 | |
461 | if (!env || !env->arch) { /* Assume local operation */ |
462 | static struct utsname uts = { .machine[0] = '\0', }; |
463 | if (uts.machine[0] == '\0' && uname(&uts) < 0) |
464 | return NULL; |
465 | arch_name = uts.machine; |
466 | } else |
467 | arch_name = env->arch; |
468 | |
469 | return normalize_arch(arch: arch_name); |
470 | } |
471 | |
472 | const char *perf_env__arch_strerrno(struct perf_env *env __maybe_unused, int err __maybe_unused) |
473 | { |
474 | #if defined(HAVE_SYSCALL_TABLE_SUPPORT) && defined(HAVE_LIBTRACEEVENT) |
475 | if (env->arch_strerrno == NULL) |
476 | env->arch_strerrno = arch_syscalls__strerrno_function(perf_env__arch(env)); |
477 | |
478 | return env->arch_strerrno ? env->arch_strerrno(err) : "no arch specific strerrno function" ; |
479 | #else |
480 | return "!(HAVE_SYSCALL_TABLE_SUPPORT && HAVE_LIBTRACEEVENT)" ; |
481 | #endif |
482 | } |
483 | |
484 | const char *perf_env__cpuid(struct perf_env *env) |
485 | { |
486 | int status; |
487 | |
488 | if (!env->cpuid) { /* Assume local operation */ |
489 | status = perf_env__read_cpuid(env); |
490 | if (status) |
491 | return NULL; |
492 | } |
493 | |
494 | return env->cpuid; |
495 | } |
496 | |
497 | int perf_env__nr_pmu_mappings(struct perf_env *env) |
498 | { |
499 | int status; |
500 | |
501 | if (!env->nr_pmu_mappings) { /* Assume local operation */ |
502 | status = perf_env__read_pmu_mappings(env); |
503 | if (status) |
504 | return 0; |
505 | } |
506 | |
507 | return env->nr_pmu_mappings; |
508 | } |
509 | |
510 | const char *perf_env__pmu_mappings(struct perf_env *env) |
511 | { |
512 | int status; |
513 | |
514 | if (!env->pmu_mappings) { /* Assume local operation */ |
515 | status = perf_env__read_pmu_mappings(env); |
516 | if (status) |
517 | return NULL; |
518 | } |
519 | |
520 | return env->pmu_mappings; |
521 | } |
522 | |
523 | int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu) |
524 | { |
525 | if (!env->nr_numa_map) { |
526 | struct numa_node *nn; |
527 | int i, nr = 0; |
528 | |
529 | for (i = 0; i < env->nr_numa_nodes; i++) { |
530 | nn = &env->numa_nodes[i]; |
531 | nr = max(nr, perf_cpu_map__max(nn->map).cpu); |
532 | } |
533 | |
534 | nr++; |
535 | |
536 | /* |
537 | * We initialize the numa_map array to prepare |
538 | * it for missing cpus, which return node -1 |
539 | */ |
540 | env->numa_map = malloc(nr * sizeof(int)); |
541 | if (!env->numa_map) |
542 | return -1; |
543 | |
544 | for (i = 0; i < nr; i++) |
545 | env->numa_map[i] = -1; |
546 | |
547 | env->nr_numa_map = nr; |
548 | |
549 | for (i = 0; i < env->nr_numa_nodes; i++) { |
550 | struct perf_cpu tmp; |
551 | int j; |
552 | |
553 | nn = &env->numa_nodes[i]; |
554 | perf_cpu_map__for_each_cpu(tmp, j, nn->map) |
555 | env->numa_map[tmp.cpu] = i; |
556 | } |
557 | } |
558 | |
559 | return cpu.cpu >= 0 && cpu.cpu < env->nr_numa_map ? env->numa_map[cpu.cpu] : -1; |
560 | } |
561 | |
562 | bool perf_env__has_pmu_mapping(struct perf_env *env, const char *pmu_name) |
563 | { |
564 | char *pmu_mapping = env->pmu_mappings, *colon; |
565 | |
566 | for (int i = 0; i < env->nr_pmu_mappings; ++i) { |
567 | if (strtoul(pmu_mapping, &colon, 0) == ULONG_MAX || *colon != ':') |
568 | goto out_error; |
569 | |
570 | pmu_mapping = colon + 1; |
571 | if (strcmp(pmu_mapping, pmu_name) == 0) |
572 | return true; |
573 | |
574 | pmu_mapping += strlen(pmu_mapping) + 1; |
575 | } |
576 | out_error: |
577 | return false; |
578 | } |
579 | |
580 | char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name, |
581 | const char *cap) |
582 | { |
583 | char *cap_eq; |
584 | int cap_size; |
585 | char **ptr; |
586 | int i, j; |
587 | |
588 | if (!pmu_name || !cap) |
589 | return NULL; |
590 | |
591 | cap_size = strlen(cap); |
592 | cap_eq = zalloc(cap_size + 2); |
593 | if (!cap_eq) |
594 | return NULL; |
595 | |
596 | memcpy(cap_eq, cap, cap_size); |
597 | cap_eq[cap_size] = '='; |
598 | |
599 | if (!strcmp(pmu_name, "cpu" )) { |
600 | for (i = 0; i < env->nr_cpu_pmu_caps; i++) { |
601 | if (!strncmp(env->cpu_pmu_caps[i], cap_eq, cap_size + 1)) { |
602 | free(cap_eq); |
603 | return &env->cpu_pmu_caps[i][cap_size + 1]; |
604 | } |
605 | } |
606 | goto out; |
607 | } |
608 | |
609 | for (i = 0; i < env->nr_pmus_with_caps; i++) { |
610 | if (strcmp(env->pmu_caps[i].pmu_name, pmu_name)) |
611 | continue; |
612 | |
613 | ptr = env->pmu_caps[i].caps; |
614 | |
615 | for (j = 0; j < env->pmu_caps[i].nr_caps; j++) { |
616 | if (!strncmp(ptr[j], cap_eq, cap_size + 1)) { |
617 | free(cap_eq); |
618 | return &ptr[j][cap_size + 1]; |
619 | } |
620 | } |
621 | } |
622 | |
623 | out: |
624 | free(cap_eq); |
625 | return NULL; |
626 | } |
627 | |