1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <errno.h> |
3 | #include <inttypes.h> |
4 | /* For the CPU_* macros */ |
5 | #include <sched.h> |
6 | |
7 | #include <sys/types.h> |
8 | #include <sys/stat.h> |
9 | #include <fcntl.h> |
10 | #include <api/fs/fs.h> |
11 | #include <linux/err.h> |
12 | #include <linux/string.h> |
13 | #include <api/fs/tracing_path.h> |
14 | #include "evsel.h" |
15 | #include "tests.h" |
16 | #include "thread_map.h" |
17 | #include <perf/cpumap.h> |
18 | #include "debug.h" |
19 | #include "stat.h" |
20 | #include "util/counts.h" |
21 | |
22 | static int test__openat_syscall_event_on_all_cpus(struct test_suite *test __maybe_unused, |
23 | int subtest __maybe_unused) |
24 | { |
25 | int err = TEST_FAIL, fd, idx; |
26 | struct perf_cpu cpu; |
27 | struct perf_cpu_map *cpus; |
28 | struct evsel *evsel; |
29 | unsigned int nr_openat_calls = 111, i; |
30 | cpu_set_t cpu_set; |
31 | struct perf_thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); |
32 | char sbuf[STRERR_BUFSIZE]; |
33 | char errbuf[BUFSIZ]; |
34 | |
35 | if (threads == NULL) { |
36 | pr_debug("thread_map__new\n" ); |
37 | return -1; |
38 | } |
39 | |
40 | cpus = perf_cpu_map__new_online_cpus(); |
41 | if (cpus == NULL) { |
42 | pr_debug("perf_cpu_map__new\n" ); |
43 | goto out_thread_map_delete; |
44 | } |
45 | |
46 | CPU_ZERO(&cpu_set); |
47 | |
48 | evsel = evsel__newtp("syscalls" , "sys_enter_openat" ); |
49 | if (IS_ERR(ptr: evsel)) { |
50 | tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls" , "sys_enter_openat" ); |
51 | pr_debug("%s\n" , errbuf); |
52 | err = TEST_SKIP; |
53 | goto out_cpu_map_delete; |
54 | } |
55 | |
56 | if (evsel__open(evsel, cpus, threads) < 0) { |
57 | pr_debug("failed to open counter: %s, " |
58 | "tweak /proc/sys/kernel/perf_event_paranoid?\n" , |
59 | str_error_r(errno, sbuf, sizeof(sbuf))); |
60 | err = TEST_SKIP; |
61 | goto out_evsel_delete; |
62 | } |
63 | |
64 | perf_cpu_map__for_each_cpu(cpu, idx, cpus) { |
65 | unsigned int ncalls = nr_openat_calls + idx; |
66 | /* |
67 | * XXX eventually lift this restriction in a way that |
68 | * keeps perf building on older glibc installations |
69 | * without CPU_ALLOC. 1024 cpus in 2010 still seems |
70 | * a reasonable upper limit tho :-) |
71 | */ |
72 | if (cpu.cpu >= CPU_SETSIZE) { |
73 | pr_debug("Ignoring CPU %d\n" , cpu.cpu); |
74 | continue; |
75 | } |
76 | |
77 | CPU_SET(cpu.cpu, &cpu_set); |
78 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { |
79 | pr_debug("sched_setaffinity() failed on CPU %d: %s " , |
80 | cpu.cpu, |
81 | str_error_r(errno, sbuf, sizeof(sbuf))); |
82 | goto out_close_fd; |
83 | } |
84 | for (i = 0; i < ncalls; ++i) { |
85 | fd = openat(0, "/etc/passwd" , O_RDONLY); |
86 | close(fd); |
87 | } |
88 | CPU_CLR(cpu.cpu, &cpu_set); |
89 | } |
90 | |
91 | evsel->core.cpus = perf_cpu_map__get(cpus); |
92 | |
93 | err = TEST_OK; |
94 | |
95 | perf_cpu_map__for_each_cpu(cpu, idx, cpus) { |
96 | unsigned int expected; |
97 | |
98 | if (cpu.cpu >= CPU_SETSIZE) |
99 | continue; |
100 | |
101 | if (evsel__read_on_cpu(evsel, idx, 0) < 0) { |
102 | pr_debug("evsel__read_on_cpu\n" ); |
103 | err = TEST_FAIL; |
104 | break; |
105 | } |
106 | |
107 | expected = nr_openat_calls + idx; |
108 | if (perf_counts(evsel->counts, idx, 0)->val != expected) { |
109 | pr_debug("evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n" , |
110 | expected, cpu.cpu, perf_counts(evsel->counts, idx, 0)->val); |
111 | err = TEST_FAIL; |
112 | } |
113 | } |
114 | |
115 | evsel__free_counts(evsel); |
116 | out_close_fd: |
117 | perf_evsel__close_fd(&evsel->core); |
118 | out_evsel_delete: |
119 | evsel__delete(evsel); |
120 | out_cpu_map_delete: |
121 | perf_cpu_map__put(cpus); |
122 | out_thread_map_delete: |
123 | perf_thread_map__put(threads); |
124 | return err; |
125 | } |
126 | |
127 | |
128 | static struct test_case tests__openat_syscall_event_on_all_cpus[] = { |
129 | TEST_CASE_REASON("Detect openat syscall event on all cpus" , |
130 | openat_syscall_event_on_all_cpus, |
131 | "permissions" ), |
132 | { .name = NULL, } |
133 | }; |
134 | |
135 | struct test_suite suite__openat_syscall_event_on_all_cpus = { |
136 | .desc = "Detect openat syscall event on all cpus" , |
137 | .test_cases = tests__openat_syscall_event_on_all_cpus, |
138 | }; |
139 | |