xref: /linux/tools/testing/selftests/bpf/prog_tests/perf_branches.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
167306f84SDaniel Xu // SPDX-License-Identifier: GPL-2.0
267306f84SDaniel Xu #define _GNU_SOURCE
367306f84SDaniel Xu #include <pthread.h>
467306f84SDaniel Xu #include <sched.h>
567306f84SDaniel Xu #include <sys/socket.h>
667306f84SDaniel Xu #include <test_progs.h>
767306f84SDaniel Xu #include "bpf/libbpf_internal.h"
867306f84SDaniel Xu #include "test_perf_branches.skel.h"
967306f84SDaniel Xu 
check_good_sample(struct test_perf_branches * skel)1067306f84SDaniel Xu static void check_good_sample(struct test_perf_branches *skel)
1167306f84SDaniel Xu {
1267306f84SDaniel Xu 	int written_global = skel->bss->written_global_out;
1367306f84SDaniel Xu 	int required_size = skel->bss->required_size_out;
1467306f84SDaniel Xu 	int written_stack = skel->bss->written_stack_out;
1567306f84SDaniel Xu 	int pbe_size = sizeof(struct perf_branch_entry);
1667306f84SDaniel Xu 	int duration = 0;
1767306f84SDaniel Xu 
1867306f84SDaniel Xu 	if (CHECK(!skel->bss->valid, "output not valid",
1967306f84SDaniel Xu 		 "no valid sample from prog"))
2067306f84SDaniel Xu 		return;
2167306f84SDaniel Xu 
2267306f84SDaniel Xu 	/*
2367306f84SDaniel Xu 	 * It's hard to validate the contents of the branch entries b/c it
2467306f84SDaniel Xu 	 * would require some kind of disassembler and also encoding the
2567306f84SDaniel Xu 	 * valid jump instructions for supported architectures. So just check
2667306f84SDaniel Xu 	 * the easy stuff for now.
2767306f84SDaniel Xu 	 */
2867306f84SDaniel Xu 	CHECK(required_size <= 0, "read_branches_size", "err %d\n", required_size);
2967306f84SDaniel Xu 	CHECK(written_stack < 0, "read_branches_stack", "err %d\n", written_stack);
3067306f84SDaniel Xu 	CHECK(written_stack % pbe_size != 0, "read_branches_stack",
3167306f84SDaniel Xu 	      "stack bytes written=%d not multiple of struct size=%d\n",
3267306f84SDaniel Xu 	      written_stack, pbe_size);
3367306f84SDaniel Xu 	CHECK(written_global < 0, "read_branches_global", "err %d\n", written_global);
3467306f84SDaniel Xu 	CHECK(written_global % pbe_size != 0, "read_branches_global",
3567306f84SDaniel Xu 	      "global bytes written=%d not multiple of struct size=%d\n",
3667306f84SDaniel Xu 	      written_global, pbe_size);
3767306f84SDaniel Xu 	CHECK(written_global < written_stack, "read_branches_size",
3867306f84SDaniel Xu 	      "written_global=%d < written_stack=%d\n", written_global, written_stack);
3967306f84SDaniel Xu }
4067306f84SDaniel Xu 
check_bad_sample(struct test_perf_branches * skel)4167306f84SDaniel Xu static void check_bad_sample(struct test_perf_branches *skel)
4267306f84SDaniel Xu {
4367306f84SDaniel Xu 	int written_global = skel->bss->written_global_out;
4467306f84SDaniel Xu 	int required_size = skel->bss->required_size_out;
4567306f84SDaniel Xu 	int written_stack = skel->bss->written_stack_out;
4667306f84SDaniel Xu 	int duration = 0;
4767306f84SDaniel Xu 
4867306f84SDaniel Xu 	if (CHECK(!skel->bss->valid, "output not valid",
4967306f84SDaniel Xu 		 "no valid sample from prog"))
5067306f84SDaniel Xu 		return;
5167306f84SDaniel Xu 
5267306f84SDaniel Xu 	CHECK((required_size != -EINVAL && required_size != -ENOENT),
5367306f84SDaniel Xu 	      "read_branches_size", "err %d\n", required_size);
5467306f84SDaniel Xu 	CHECK((written_stack != -EINVAL && written_stack != -ENOENT),
5567306f84SDaniel Xu 	      "read_branches_stack", "written %d\n", written_stack);
5667306f84SDaniel Xu 	CHECK((written_global != -EINVAL && written_global != -ENOENT),
5767306f84SDaniel Xu 	      "read_branches_global", "written %d\n", written_global);
5867306f84SDaniel Xu }
5967306f84SDaniel Xu 
test_perf_branches_common(int perf_fd,void (* cb)(struct test_perf_branches *))6067306f84SDaniel Xu static void test_perf_branches_common(int perf_fd,
6167306f84SDaniel Xu 				      void (*cb)(struct test_perf_branches *))
6267306f84SDaniel Xu {
6367306f84SDaniel Xu 	struct test_perf_branches *skel;
6467306f84SDaniel Xu 	int err, i, duration = 0;
6567306f84SDaniel Xu 	bool detached = false;
6667306f84SDaniel Xu 	struct bpf_link *link;
6767306f84SDaniel Xu 	volatile int j = 0;
6867306f84SDaniel Xu 	cpu_set_t cpu_set;
6967306f84SDaniel Xu 
7067306f84SDaniel Xu 	skel = test_perf_branches__open_and_load();
7167306f84SDaniel Xu 	if (CHECK(!skel, "test_perf_branches_load",
7267306f84SDaniel Xu 		  "perf_branches skeleton failed\n"))
7367306f84SDaniel Xu 		return;
7467306f84SDaniel Xu 
7567306f84SDaniel Xu 	/* attach perf_event */
7667306f84SDaniel Xu 	link = bpf_program__attach_perf_event(skel->progs.perf_branches, perf_fd);
77bad2e478SAndrii Nakryiko 	if (!ASSERT_OK_PTR(link, "attach_perf_event"))
7867306f84SDaniel Xu 		goto out_destroy_skel;
7967306f84SDaniel Xu 
8067306f84SDaniel Xu 	/* generate some branches on cpu 0 */
8167306f84SDaniel Xu 	CPU_ZERO(&cpu_set);
8267306f84SDaniel Xu 	CPU_SET(0, &cpu_set);
8367306f84SDaniel Xu 	err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
8467306f84SDaniel Xu 	if (CHECK(err, "set_affinity", "cpu #0, err %d\n", err))
8567306f84SDaniel Xu 		goto out_destroy;
8667306f84SDaniel Xu 	/* spin the loop for a while (random high number) */
8767306f84SDaniel Xu 	for (i = 0; i < 1000000; ++i)
8867306f84SDaniel Xu 		++j;
8967306f84SDaniel Xu 
9067306f84SDaniel Xu 	test_perf_branches__detach(skel);
9167306f84SDaniel Xu 	detached = true;
9267306f84SDaniel Xu 
9367306f84SDaniel Xu 	cb(skel);
9467306f84SDaniel Xu out_destroy:
9567306f84SDaniel Xu 	bpf_link__destroy(link);
9667306f84SDaniel Xu out_destroy_skel:
9767306f84SDaniel Xu 	if (!detached)
9867306f84SDaniel Xu 		test_perf_branches__detach(skel);
9967306f84SDaniel Xu 	test_perf_branches__destroy(skel);
10067306f84SDaniel Xu }
10167306f84SDaniel Xu 
test_perf_branches_hw(void)10267306f84SDaniel Xu static void test_perf_branches_hw(void)
10367306f84SDaniel Xu {
10467306f84SDaniel Xu 	struct perf_event_attr attr = {0};
10567306f84SDaniel Xu 	int duration = 0;
10667306f84SDaniel Xu 	int pfd;
10767306f84SDaniel Xu 
10867306f84SDaniel Xu 	/* create perf event */
10967306f84SDaniel Xu 	attr.size = sizeof(attr);
11067306f84SDaniel Xu 	attr.type = PERF_TYPE_HARDWARE;
11167306f84SDaniel Xu 	attr.config = PERF_COUNT_HW_CPU_CYCLES;
11267306f84SDaniel Xu 	attr.freq = 1;
113*d4b54054SMykola Lysenko 	attr.sample_freq = 1000;
11467306f84SDaniel Xu 	attr.sample_type = PERF_SAMPLE_BRANCH_STACK;
11567306f84SDaniel Xu 	attr.branch_sample_type = PERF_SAMPLE_BRANCH_USER | PERF_SAMPLE_BRANCH_ANY;
11667306f84SDaniel Xu 	pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
11767306f84SDaniel Xu 
11867306f84SDaniel Xu 	/*
11967306f84SDaniel Xu 	 * Some setups don't support branch records (virtual machines, !x86),
12067306f84SDaniel Xu 	 * so skip test in this case.
12167306f84SDaniel Xu 	 */
122bad2e478SAndrii Nakryiko 	if (pfd < 0) {
12367306f84SDaniel Xu 		if (errno == ENOENT || errno == EOPNOTSUPP) {
12467306f84SDaniel Xu 			printf("%s:SKIP:no PERF_SAMPLE_BRANCH_STACK\n",
12567306f84SDaniel Xu 			       __func__);
12667306f84SDaniel Xu 			test__skip();
12767306f84SDaniel Xu 			return;
12867306f84SDaniel Xu 		}
12967306f84SDaniel Xu 		if (CHECK(pfd < 0, "perf_event_open", "err %d errno %d\n",
13067306f84SDaniel Xu 			  pfd, errno))
13167306f84SDaniel Xu 			return;
13267306f84SDaniel Xu 	}
13367306f84SDaniel Xu 
13467306f84SDaniel Xu 	test_perf_branches_common(pfd, check_good_sample);
13567306f84SDaniel Xu 
13667306f84SDaniel Xu 	close(pfd);
13767306f84SDaniel Xu }
13867306f84SDaniel Xu 
13967306f84SDaniel Xu /*
14067306f84SDaniel Xu  * Tests negative case -- run bpf_read_branch_records() on improperly configured
14167306f84SDaniel Xu  * perf event.
14267306f84SDaniel Xu  */
test_perf_branches_no_hw(void)14367306f84SDaniel Xu static void test_perf_branches_no_hw(void)
14467306f84SDaniel Xu {
14567306f84SDaniel Xu 	struct perf_event_attr attr = {0};
14667306f84SDaniel Xu 	int duration = 0;
14767306f84SDaniel Xu 	int pfd;
14867306f84SDaniel Xu 
14967306f84SDaniel Xu 	/* create perf event */
15067306f84SDaniel Xu 	attr.size = sizeof(attr);
15167306f84SDaniel Xu 	attr.type = PERF_TYPE_SOFTWARE;
15267306f84SDaniel Xu 	attr.config = PERF_COUNT_SW_CPU_CLOCK;
15367306f84SDaniel Xu 	attr.freq = 1;
154*d4b54054SMykola Lysenko 	attr.sample_freq = 1000;
15567306f84SDaniel Xu 	pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
15667306f84SDaniel Xu 	if (CHECK(pfd < 0, "perf_event_open", "err %d\n", pfd))
15767306f84SDaniel Xu 		return;
15867306f84SDaniel Xu 
15967306f84SDaniel Xu 	test_perf_branches_common(pfd, check_bad_sample);
16067306f84SDaniel Xu 
16167306f84SDaniel Xu 	close(pfd);
16267306f84SDaniel Xu }
16367306f84SDaniel Xu 
test_perf_branches(void)16467306f84SDaniel Xu void test_perf_branches(void)
16567306f84SDaniel Xu {
16667306f84SDaniel Xu 	if (test__start_subtest("perf_branches_hw"))
16767306f84SDaniel Xu 		test_perf_branches_hw();
16867306f84SDaniel Xu 	if (test__start_subtest("perf_branches_no_hw"))
16967306f84SDaniel Xu 		test_perf_branches_no_hw();
17067306f84SDaniel Xu }
171