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

Configure Feed

Select the types of activity you want to include in your feed.

perf trace: Reorganize syscalls

Identify struct syscall information in the syscalls table by a machine
type and syscall number, not just system call number. Having the
machine type means that 32-bit system calls can be differentiated from
64-bit ones on a machine capable of both. Having a table for all
machine types and all system call numbers would be too large, so
maintain a sorted array of system calls as they are encountered.

Signed-off-by: Ian Rogers <irogers@google.com>
Reviewed-by: Howard Chu <howardchu95@gmail.com>
Reviewed-by: Charlie Jenkins <charlie@rivosinc.com>
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Link: https://lore.kernel.org/r/20250319050741.269828-5-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
3d94b844 af472d3c

+132 -65
+132 -65
tools/perf/builtin-trace.c
··· 66 66 #include "syscalltbl.h" 67 67 #include "../perf.h" 68 68 #include "trace_augment.h" 69 + #include "dwarf-regs.h" 69 70 70 71 #include <errno.h> 71 72 #include <inttypes.h> ··· 87 86 88 87 #include <linux/ctype.h> 89 88 #include <perf/mmap.h> 89 + #include <tools/libc_compat.h> 90 90 91 91 #ifdef HAVE_LIBTRACEEVENT 92 92 #include <event-parse.h> ··· 151 149 struct perf_tool tool; 152 150 struct syscalltbl *sctbl; 153 151 struct { 152 + /** Sorted sycall numbers used by the trace. */ 154 153 struct syscall *table; 154 + /** Size of table. */ 155 + size_t table_size; 155 156 struct { 156 157 struct evsel *sys_enter, 157 158 *sys_exit, ··· 1459 1454 return __syscall_fmt__find_by_alias(syscall_fmts, nmemb, alias); 1460 1455 } 1461 1456 1462 - /* 1463 - * is_exit: is this "exit" or "exit_group"? 1464 - * is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter. 1465 - * args_size: sum of the sizes of the syscall arguments, anything after that is augmented stuff: pathname for openat, etc. 1466 - * nonexistent: Just a hole in the syscall table, syscall id not allocated 1457 + /** 1458 + * struct syscall 1467 1459 */ 1468 1460 struct syscall { 1461 + /** @e_machine: The ELF machine associated with the entry. */ 1462 + int e_machine; 1463 + /** @id: id value from the tracepoint, the system call number. */ 1464 + int id; 1469 1465 struct tep_event *tp_format; 1470 1466 int nr_args; 1467 + /** 1468 + * @args_size: sum of the sizes of the syscall arguments, anything 1469 + * after that is augmented stuff: pathname for openat, etc. 1470 + */ 1471 + 1471 1472 int args_size; 1472 1473 struct { 1473 1474 struct bpf_program *sys_enter, 1474 1475 *sys_exit; 1475 1476 } bpf_prog; 1477 + /** @is_exit: is this "exit" or "exit_group"? */ 1476 1478 bool is_exit; 1479 + /** 1480 + * @is_open: is this "open" or "openat"? To associate the fd returned in 1481 + * sys_exit with the pathname in sys_enter. 1482 + */ 1477 1483 bool is_open; 1484 + /** 1485 + * @nonexistent: Name lookup failed. Just a hole in the syscall table, 1486 + * syscall id not allocated. 1487 + */ 1478 1488 bool nonexistent; 1479 1489 bool use_btf; 1480 1490 struct tep_format_field *args; ··· 2127 2107 return 0; 2128 2108 } 2129 2109 2130 - static int trace__read_syscall_info(struct trace *trace, int id) 2110 + static int syscall__read_info(struct syscall *sc, struct trace *trace) 2131 2111 { 2132 2112 char tp_name[128]; 2133 - struct syscall *sc; 2134 - const char *name = syscalltbl__name(trace->sctbl, id); 2113 + const char *name; 2135 2114 int err; 2136 2115 2137 - if (trace->syscalls.table == NULL) { 2138 - trace->syscalls.table = calloc(trace->sctbl->syscalls.max_id + 1, sizeof(*sc)); 2139 - if (trace->syscalls.table == NULL) 2140 - return -ENOMEM; 2141 - } 2142 - sc = trace->syscalls.table + id; 2143 2116 if (sc->nonexistent) 2144 2117 return -EEXIST; 2145 2118 2119 + if (sc->name) { 2120 + /* Info already read. */ 2121 + return 0; 2122 + } 2123 + 2124 + name = syscalltbl__name(trace->sctbl, sc->id); 2146 2125 if (name == NULL) { 2147 2126 sc->nonexistent = true; 2148 2127 return -EEXIST; ··· 2164 2145 */ 2165 2146 if (IS_ERR(sc->tp_format)) { 2166 2147 sc->nonexistent = true; 2167 - return PTR_ERR(sc->tp_format); 2148 + err = PTR_ERR(sc->tp_format); 2149 + sc->tp_format = NULL; 2150 + return err; 2168 2151 } 2169 2152 2170 2153 /* 2171 2154 * The tracepoint format contains __syscall_nr field, so it's one more 2172 2155 * than the actual number of syscall arguments. 2173 2156 */ 2174 - if (syscall__alloc_arg_fmts(sc, IS_ERR(sc->tp_format) ? 2175 - RAW_SYSCALL_ARGS_NUM : sc->tp_format->format.nr_fields - 1)) 2157 + if (syscall__alloc_arg_fmts(sc, sc->tp_format->format.nr_fields - 1)) 2176 2158 return -ENOMEM; 2177 2159 2178 2160 sc->args = sc->tp_format->format.fields; ··· 2462 2442 return printed; 2463 2443 } 2464 2444 2445 + static void syscall__init(struct syscall *sc, int e_machine, int id) 2446 + { 2447 + memset(sc, 0, sizeof(*sc)); 2448 + sc->e_machine = e_machine; 2449 + sc->id = id; 2450 + } 2451 + 2452 + static void syscall__exit(struct syscall *sc) 2453 + { 2454 + if (!sc) 2455 + return; 2456 + 2457 + zfree(&sc->arg_fmt); 2458 + } 2459 + 2460 + static int syscall__cmp(const void *va, const void *vb) 2461 + { 2462 + const struct syscall *a = va, *b = vb; 2463 + 2464 + if (a->e_machine != b->e_machine) 2465 + return a->e_machine - b->e_machine; 2466 + 2467 + return a->id - b->id; 2468 + } 2469 + 2470 + static struct syscall *trace__find_syscall(struct trace *trace, int e_machine, int id) 2471 + { 2472 + struct syscall key = { 2473 + .e_machine = e_machine, 2474 + .id = id, 2475 + }; 2476 + struct syscall *sc, *tmp; 2477 + 2478 + if (trace->syscalls.table) { 2479 + sc = bsearch(&key, trace->syscalls.table, trace->syscalls.table_size, 2480 + sizeof(struct syscall), syscall__cmp); 2481 + if (sc) 2482 + return sc; 2483 + } 2484 + 2485 + tmp = reallocarray(trace->syscalls.table, trace->syscalls.table_size + 1, 2486 + sizeof(struct syscall)); 2487 + if (!tmp) 2488 + return NULL; 2489 + 2490 + trace->syscalls.table = tmp; 2491 + sc = &trace->syscalls.table[trace->syscalls.table_size++]; 2492 + syscall__init(sc, e_machine, id); 2493 + qsort(trace->syscalls.table, trace->syscalls.table_size, sizeof(struct syscall), 2494 + syscall__cmp); 2495 + sc = bsearch(&key, trace->syscalls.table, trace->syscalls.table_size, 2496 + sizeof(struct syscall), syscall__cmp); 2497 + return sc; 2498 + } 2499 + 2465 2500 typedef int (*tracepoint_handler)(struct trace *trace, struct evsel *evsel, 2466 2501 union perf_event *event, 2467 2502 struct perf_sample *sample); 2468 2503 2469 - static struct syscall *trace__syscall_info(struct trace *trace, 2470 - struct evsel *evsel, int id) 2504 + static struct syscall *trace__syscall_info(struct trace *trace, struct evsel *evsel, 2505 + int e_machine, int id) 2471 2506 { 2507 + struct syscall *sc; 2472 2508 int err = 0; 2473 2509 2474 2510 if (id < 0) { ··· 2549 2473 2550 2474 err = -EINVAL; 2551 2475 2552 - if (id > trace->sctbl->syscalls.max_id) { 2553 - goto out_cant_read; 2554 - } 2476 + sc = trace__find_syscall(trace, e_machine, id); 2477 + if (sc) 2478 + err = syscall__read_info(sc, trace); 2555 2479 2556 - if ((trace->syscalls.table == NULL || trace->syscalls.table[id].name == NULL) && 2557 - (err = trace__read_syscall_info(trace, id)) != 0) 2558 - goto out_cant_read; 2559 - 2560 - if (trace->syscalls.table && trace->syscalls.table[id].nonexistent) 2561 - goto out_cant_read; 2562 - 2563 - return &trace->syscalls.table[id]; 2564 - 2565 - out_cant_read: 2566 - if (verbose > 0) { 2480 + if (err && verbose > 0) { 2567 2481 char sbuf[STRERR_BUFSIZE]; 2568 - fprintf(trace->output, "Problems reading syscall %d: %d (%s)", id, -err, str_error_r(-err, sbuf, sizeof(sbuf))); 2569 - if (id <= trace->sctbl->syscalls.max_id && trace->syscalls.table[id].name != NULL) 2570 - fprintf(trace->output, "(%s)", trace->syscalls.table[id].name); 2482 + 2483 + fprintf(trace->output, "Problems reading syscall %d: %d (%s)", id, -err, 2484 + str_error_r(-err, sbuf, sizeof(sbuf))); 2485 + if (sc && sc->name) 2486 + fprintf(trace->output, "(%s)", sc->name); 2571 2487 fputs(" information\n", trace->output); 2572 2488 } 2573 - return NULL; 2489 + return err ? NULL : sc; 2574 2490 } 2575 2491 2576 2492 struct syscall_stats { ··· 2711 2643 return NULL; 2712 2644 } 2713 2645 2714 - static void syscall__exit(struct syscall *sc) 2715 - { 2716 - if (!sc) 2717 - return; 2718 - 2719 - zfree(&sc->arg_fmt); 2720 - } 2721 - 2722 2646 static int trace__sys_enter(struct trace *trace, struct evsel *evsel, 2723 2647 union perf_event *event __maybe_unused, 2724 2648 struct perf_sample *sample) ··· 2722 2662 int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; 2723 2663 int augmented_args_size = 0; 2724 2664 void *augmented_args = NULL; 2725 - struct syscall *sc = trace__syscall_info(trace, evsel, id); 2665 + struct syscall *sc = trace__syscall_info(trace, evsel, EM_HOST, id); 2726 2666 struct thread_trace *ttrace; 2727 2667 2728 2668 if (sc == NULL) ··· 2796 2736 struct thread_trace *ttrace; 2797 2737 struct thread *thread; 2798 2738 int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1; 2799 - struct syscall *sc = trace__syscall_info(trace, evsel, id); 2739 + struct syscall *sc = trace__syscall_info(trace, evsel, EM_HOST, id); 2800 2740 char msg[1024]; 2801 2741 void *args, *augmented_args = NULL; 2802 2742 int augmented_args_size; ··· 2871 2811 struct thread *thread; 2872 2812 int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0, printed = 0; 2873 2813 int alignment = trace->args_alignment; 2874 - struct syscall *sc = trace__syscall_info(trace, evsel, id); 2814 + struct syscall *sc = trace__syscall_info(trace, evsel, EM_HOST, id); 2875 2815 struct thread_trace *ttrace; 2876 2816 2877 2817 if (sc == NULL) ··· 3224 3164 3225 3165 if (evsel == trace->syscalls.events.bpf_output) { 3226 3166 int id = perf_evsel__sc_tp_uint(evsel, id, sample); 3227 - struct syscall *sc = trace__syscall_info(trace, evsel, id); 3167 + struct syscall *sc = trace__syscall_info(trace, evsel, EM_HOST, id); 3228 3168 3229 3169 if (sc) { 3230 3170 fprintf(trace->output, "%s(", sc->name); ··· 3733 3673 3734 3674 static void trace__init_syscall_bpf_progs(struct trace *trace, int id) 3735 3675 { 3736 - struct syscall *sc = trace__syscall_info(trace, NULL, id); 3676 + struct syscall *sc = trace__syscall_info(trace, NULL, EM_HOST, id); 3737 3677 3738 3678 if (sc == NULL) 3739 3679 return; ··· 3744 3684 3745 3685 static int trace__bpf_prog_sys_enter_fd(struct trace *trace, int id) 3746 3686 { 3747 - struct syscall *sc = trace__syscall_info(trace, NULL, id); 3687 + struct syscall *sc = trace__syscall_info(trace, NULL, EM_HOST, id); 3748 3688 return sc ? bpf_program__fd(sc->bpf_prog.sys_enter) : bpf_program__fd(trace->skel->progs.syscall_unaugmented); 3749 3689 } 3750 3690 3751 3691 static int trace__bpf_prog_sys_exit_fd(struct trace *trace, int id) 3752 3692 { 3753 - struct syscall *sc = trace__syscall_info(trace, NULL, id); 3693 + struct syscall *sc = trace__syscall_info(trace, NULL, EM_HOST, id); 3754 3694 return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(trace->skel->progs.syscall_unaugmented); 3755 3695 } 3756 3696 3757 3697 static int trace__bpf_sys_enter_beauty_map(struct trace *trace, int key, unsigned int *beauty_array) 3758 3698 { 3759 3699 struct tep_format_field *field; 3760 - struct syscall *sc = trace__syscall_info(trace, NULL, key); 3700 + struct syscall *sc = trace__syscall_info(trace, NULL, EM_HOST, key); 3761 3701 const struct btf_type *bt; 3762 3702 char *struct_offset, *tmp, name[32]; 3763 3703 bool can_augment = false; ··· 3839 3779 return -1; 3840 3780 } 3841 3781 3842 - static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace, struct syscall *sc) 3782 + static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace, struct syscall *_sc) 3843 3783 { 3784 + struct syscall sc = *_sc; /* Copy as trace__syscall_info may invalidate pointer. */ 3844 3785 struct tep_format_field *field, *candidate_field; 3845 3786 /* 3846 3787 * We're only interested in syscalls that have a pointer: 3847 3788 */ 3848 - for (field = sc->args; field; field = field->next) { 3789 + for (field = sc.args; field; field = field->next) { 3849 3790 if (field->flags & TEP_FIELD_IS_POINTER) 3850 3791 goto try_to_find_pair; 3851 3792 } ··· 3856 3795 try_to_find_pair: 3857 3796 for (int i = 0; i < trace->sctbl->syscalls.nr_entries; ++i) { 3858 3797 int id = syscalltbl__id_at_idx(trace->sctbl, i); 3859 - struct syscall *pair = trace__syscall_info(trace, NULL, id); 3798 + /* calling trace__syscall_info() may invalidate '_sc' */ 3799 + struct syscall *pair = trace__syscall_info(trace, NULL, sc.e_machine, id); 3860 3800 struct bpf_program *pair_prog; 3861 3801 bool is_candidate = false; 3862 3802 3863 - if (pair == NULL || pair == sc || 3803 + if (pair == NULL || pair->id == sc.id || 3864 3804 pair->bpf_prog.sys_enter == trace->skel->progs.syscall_unaugmented) 3865 3805 continue; 3866 3806 3867 - for (field = sc->args, candidate_field = pair->args; 3807 + for (field = sc.args, candidate_field = pair->args; 3868 3808 field && candidate_field; field = field->next, candidate_field = candidate_field->next) { 3869 3809 bool is_pointer = field->flags & TEP_FIELD_IS_POINTER, 3870 3810 candidate_is_pointer = candidate_field->flags & TEP_FIELD_IS_POINTER; ··· 3932 3870 goto next_candidate; 3933 3871 } 3934 3872 3935 - pr_debug("Reusing \"%s\" BPF sys_enter augmenter for \"%s\"\n", pair->name, sc->name); 3873 + pr_debug("Reusing \"%s\" BPF sys_enter augmenter for \"%s\"\n", pair->name, sc.name); 3936 3874 return pair_prog; 3937 3875 next_candidate: 3938 3876 continue; ··· 4007 3945 */ 4008 3946 for (int i = 0; i < trace->sctbl->syscalls.nr_entries; ++i) { 4009 3947 int key = syscalltbl__id_at_idx(trace->sctbl, i); 4010 - struct syscall *sc = trace__syscall_info(trace, NULL, key); 3948 + struct syscall *sc = trace__syscall_info(trace, NULL, EM_HOST, key); 4011 3949 struct bpf_program *pair_prog; 4012 3950 int prog_fd; 4013 3951 ··· 4028 3966 pair_prog = trace__find_usable_bpf_prog_entry(trace, sc); 4029 3967 if (pair_prog == NULL) 4030 3968 continue; 4031 - 3969 + /* 3970 + * Get syscall info again as find usable entry above might 3971 + * modify the syscall table and shuffle it. 3972 + */ 3973 + sc = trace__syscall_info(trace, NULL, EM_HOST, key); 4032 3974 sc->bpf_prog.sys_enter = pair_prog; 4033 3975 4034 3976 /* ··· 4827 4761 pct = avg ? 100.0 * stddev_stats(&stats->stats) / avg : 0.0; 4828 4762 avg /= NSEC_PER_MSEC; 4829 4763 4830 - sc = &trace->syscalls.table[entry->syscall]; 4764 + sc = trace__syscall_info(trace, /*evsel=*/NULL, EM_HOST, entry->syscall); 4765 + if (!sc) 4766 + continue; 4767 + 4831 4768 printed += fprintf(fp, " %-15s", sc->name); 4832 4769 printed += fprintf(fp, " %8" PRIu64 " %6" PRIu64 " %9.3f %9.3f %9.3f", 4833 4770 n, stats->nr_failures, entry->msecs, min, avg); ··· 5287 5218 5288 5219 static void trace__exit(struct trace *trace) 5289 5220 { 5290 - int i; 5291 - 5292 5221 strlist__delete(trace->ev_qualifier); 5293 5222 zfree(&trace->ev_qualifier_ids.entries); 5294 5223 if (trace->syscalls.table) { 5295 - for (i = 0; i <= trace->sctbl->syscalls.max_id; i++) 5224 + for (size_t i = 0; i < trace->syscalls.table_size; i++) 5296 5225 syscall__exit(&trace->syscalls.table[i]); 5297 5226 zfree(&trace->syscalls.table); 5298 5227 }