Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2#define _GNU_SOURCE
3#include <pthread.h>
4#include <sched.h>
5#include <sys/socket.h>
6#include <test_progs.h>
7#include "bpf/libbpf_internal.h"
8#include "test_perf_branches.skel.h"
9
10static void check_good_sample(struct test_perf_branches *skel)
11{
12 int written_global = skel->bss->written_global_out;
13 int required_size = skel->bss->required_size_out;
14 int written_stack = skel->bss->written_stack_out;
15 int pbe_size = sizeof(struct perf_branch_entry);
16 int duration = 0;
17
18 if (CHECK(!skel->bss->run_cnt, "invalid run_cnt",
19 "checked sample validity before prog run"))
20 return;
21
22 if (CHECK(!skel->bss->valid, "output not valid",
23 "no valid sample from prog"))
24 return;
25
26 /*
27 * It's hard to validate the contents of the branch entries b/c it
28 * would require some kind of disassembler and also encoding the
29 * valid jump instructions for supported architectures. So just check
30 * the easy stuff for now.
31 */
32 CHECK(required_size <= 0, "read_branches_size", "err %d\n", required_size);
33 CHECK(written_stack < 0, "read_branches_stack", "err %d\n", written_stack);
34 CHECK(written_stack % pbe_size != 0, "read_branches_stack",
35 "stack bytes written=%d not multiple of struct size=%d\n",
36 written_stack, pbe_size);
37 CHECK(written_global < 0, "read_branches_global", "err %d\n", written_global);
38 CHECK(written_global % pbe_size != 0, "read_branches_global",
39 "global bytes written=%d not multiple of struct size=%d\n",
40 written_global, pbe_size);
41 CHECK(written_global < written_stack, "read_branches_size",
42 "written_global=%d < written_stack=%d\n", written_global, written_stack);
43}
44
45static void check_bad_sample(struct test_perf_branches *skel)
46{
47 int written_global = skel->bss->written_global_out;
48 int required_size = skel->bss->required_size_out;
49 int written_stack = skel->bss->written_stack_out;
50 int duration = 0;
51
52 if (CHECK(!skel->bss->run_cnt, "invalid run_cnt",
53 "checked sample validity before prog run"))
54 return;
55
56 if (CHECK(!skel->bss->valid, "output not valid",
57 "no valid sample from prog"))
58 return;
59
60 CHECK((required_size != -EINVAL && required_size != -ENOENT),
61 "read_branches_size", "err %d\n", required_size);
62 CHECK((written_stack != -EINVAL && written_stack != -ENOENT),
63 "read_branches_stack", "written %d\n", written_stack);
64 CHECK((written_global != -EINVAL && written_global != -ENOENT),
65 "read_branches_global", "written %d\n", written_global);
66}
67
68static void test_perf_branches_common(int perf_fd,
69 void (*cb)(struct test_perf_branches *))
70{
71 struct test_perf_branches *skel;
72 int err, i, duration = 0;
73 bool detached = false;
74 struct bpf_link *link;
75 volatile int j = 0;
76 cpu_set_t cpu_set;
77
78 skel = test_perf_branches__open_and_load();
79 if (CHECK(!skel, "test_perf_branches_load",
80 "perf_branches skeleton failed\n"))
81 return;
82
83 /* attach perf_event */
84 link = bpf_program__attach_perf_event(skel->progs.perf_branches, perf_fd);
85 if (!ASSERT_OK_PTR(link, "attach_perf_event"))
86 goto out_destroy_skel;
87
88 /* generate some branches on cpu 0 */
89 CPU_ZERO(&cpu_set);
90 CPU_SET(0, &cpu_set);
91 err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
92 if (CHECK(err, "set_affinity", "cpu #0, err %d\n", err))
93 goto out_destroy;
94
95 /* Spin the loop for a while by using a high iteration count, and by
96 * checking whether the specific run count marker has been explicitly
97 * incremented at least once by the backing perf_event BPF program.
98 */
99 for (i = 0; i < 100000000 && !*(volatile int *)&skel->bss->run_cnt; ++i)
100 ++j;
101
102 test_perf_branches__detach(skel);
103 detached = true;
104
105 cb(skel);
106out_destroy:
107 bpf_link__destroy(link);
108out_destroy_skel:
109 if (!detached)
110 test_perf_branches__detach(skel);
111 test_perf_branches__destroy(skel);
112}
113
114static void test_perf_branches_hw(void)
115{
116 struct perf_event_attr attr = {0};
117 int duration = 0;
118 int pfd;
119
120 /* create perf event */
121 attr.size = sizeof(attr);
122 attr.type = PERF_TYPE_HARDWARE;
123 attr.config = PERF_COUNT_HW_CPU_CYCLES;
124 attr.freq = 1;
125 attr.sample_freq = 1000;
126 attr.sample_type = PERF_SAMPLE_BRANCH_STACK;
127 attr.branch_sample_type = PERF_SAMPLE_BRANCH_USER | PERF_SAMPLE_BRANCH_ANY;
128 pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
129
130 /*
131 * Some setups don't support LBR (virtual machines, !x86, AMD Milan Zen
132 * 3 which only supports BRS), so skip test in this case.
133 */
134 if (pfd < 0) {
135 if (errno == ENOENT || errno == EOPNOTSUPP || errno == EINVAL) {
136 printf("%s:SKIP:no PERF_SAMPLE_BRANCH_STACK\n",
137 __func__);
138 test__skip();
139 return;
140 }
141 if (CHECK(pfd < 0, "perf_event_open", "err %d errno %d\n",
142 pfd, errno))
143 return;
144 }
145
146 test_perf_branches_common(pfd, check_good_sample);
147
148 close(pfd);
149}
150
151/*
152 * Tests negative case -- run bpf_read_branch_records() on improperly configured
153 * perf event.
154 */
155static void test_perf_branches_no_hw(void)
156{
157 struct perf_event_attr attr = {0};
158 int duration = 0;
159 int pfd;
160
161 /* create perf event */
162 attr.size = sizeof(attr);
163 attr.type = PERF_TYPE_SOFTWARE;
164 attr.config = PERF_COUNT_SW_CPU_CLOCK;
165 attr.freq = 1;
166 attr.sample_freq = 1000;
167 pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
168 if (CHECK(pfd < 0, "perf_event_open", "err %d\n", pfd))
169 return;
170
171 test_perf_branches_common(pfd, check_bad_sample);
172
173 close(pfd);
174}
175
176void test_perf_branches(void)
177{
178 if (test__start_subtest("perf_branches_hw"))
179 test_perf_branches_hw();
180 if (test__start_subtest("perf_branches_no_hw"))
181 test_perf_branches_no_hw();
182}