| .. | .. | 
|---|
|  | 1 | +// SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 1 | 2 | /* Copyright (c) 2016 Facebook | 
|---|
| 2 |  | - * | 
|---|
| 3 |  | - * This program is free software; you can redistribute it and/or | 
|---|
| 4 |  | - * modify it under the terms of version 2 of the GNU General Public | 
|---|
| 5 |  | - * License as published by the Free Software Foundation. | 
|---|
| 6 | 3 | */ | 
|---|
| 7 | 4 | #include <stdio.h> | 
|---|
| 8 | 5 | #include <unistd.h> | 
|---|
| 9 | 6 | #include <stdlib.h> | 
|---|
| 10 | 7 | #include <stdbool.h> | 
|---|
| 11 | 8 | #include <string.h> | 
|---|
| 12 |  | -#include <fcntl.h> | 
|---|
| 13 |  | -#include <poll.h> | 
|---|
| 14 |  | -#include <sys/ioctl.h> | 
|---|
| 15 | 9 | #include <linux/perf_event.h> | 
|---|
| 16 | 10 | #include <linux/bpf.h> | 
|---|
| 17 | 11 | #include <signal.h> | 
|---|
| 18 |  | -#include <assert.h> | 
|---|
| 19 | 12 | #include <errno.h> | 
|---|
| 20 | 13 | #include <sys/resource.h> | 
|---|
| 21 |  | -#include "libbpf.h" | 
|---|
| 22 |  | -#include "bpf_load.h" | 
|---|
|  | 14 | +#include <bpf/bpf.h> | 
|---|
|  | 15 | +#include <bpf/libbpf.h> | 
|---|
| 23 | 16 | #include "perf-sys.h" | 
|---|
| 24 | 17 | #include "trace_helpers.h" | 
|---|
| 25 | 18 |  | 
|---|
| 26 | 19 | #define SAMPLE_FREQ 50 | 
|---|
| 27 | 20 |  | 
|---|
|  | 21 | +static int pid; | 
|---|
|  | 22 | +/* counts, stackmap */ | 
|---|
|  | 23 | +static int map_fd[2]; | 
|---|
|  | 24 | +struct bpf_program *prog; | 
|---|
| 28 | 25 | static bool sys_read_seen, sys_write_seen; | 
|---|
| 29 | 26 |  | 
|---|
| 30 | 27 | static void print_ksym(__u64 addr) | 
|---|
| .. | .. | 
|---|
| 34 | 31 | if (!addr) | 
|---|
| 35 | 32 | return; | 
|---|
| 36 | 33 | sym = ksym_search(addr); | 
|---|
|  | 34 | +	if (!sym) { | 
|---|
|  | 35 | +		printf("ksym not found. Is kallsyms loaded?\n"); | 
|---|
|  | 36 | +		return; | 
|---|
|  | 37 | +	} | 
|---|
|  | 38 | + | 
|---|
| 37 | 39 | printf("%s;", sym->name); | 
|---|
| 38 | 40 | if (!strstr(sym->name, "sys_read")) | 
|---|
| 39 | 41 | sys_read_seen = true; | 
|---|
| .. | .. | 
|---|
| 89 | 91 | } | 
|---|
| 90 | 92 | } | 
|---|
| 91 | 93 |  | 
|---|
| 92 |  | -static void int_exit(int sig) | 
|---|
|  | 94 | +static void err_exit(int err) | 
|---|
| 93 | 95 | { | 
|---|
| 94 |  | -	kill(0, SIGKILL); | 
|---|
| 95 |  | -	exit(0); | 
|---|
|  | 96 | +	kill(pid, SIGKILL); | 
|---|
|  | 97 | +	exit(err); | 
|---|
| 96 | 98 | } | 
|---|
| 97 | 99 |  | 
|---|
| 98 | 100 | static void print_stacks(void) | 
|---|
| .. | .. | 
|---|
| 100 | 102 | struct key_t key = {}, next_key; | 
|---|
| 101 | 103 | __u64 value; | 
|---|
| 102 | 104 | __u32 stackid = 0, next_id; | 
|---|
| 103 |  | -	int fd = map_fd[0], stack_map = map_fd[1]; | 
|---|
|  | 105 | +	int error = 1, fd = map_fd[0], stack_map = map_fd[1]; | 
|---|
| 104 | 106 |  | 
|---|
| 105 | 107 | sys_read_seen = sys_write_seen = false; | 
|---|
| 106 | 108 | while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { | 
|---|
| .. | .. | 
|---|
| 112 | 114 | printf("\n"); | 
|---|
| 113 | 115 | if (!sys_read_seen || !sys_write_seen) { | 
|---|
| 114 | 116 | printf("BUG kernel stack doesn't contain sys_read() and sys_write()\n"); | 
|---|
| 115 |  | -		int_exit(0); | 
|---|
|  | 117 | +		err_exit(error); | 
|---|
| 116 | 118 | } | 
|---|
| 117 | 119 |  | 
|---|
| 118 | 120 | /* clear stack map */ | 
|---|
| .. | .. | 
|---|
| 134 | 136 |  | 
|---|
| 135 | 137 | static void test_perf_event_all_cpu(struct perf_event_attr *attr) | 
|---|
| 136 | 138 | { | 
|---|
| 137 |  | -	int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); | 
|---|
| 138 |  | -	int *pmu_fd = malloc(nr_cpus * sizeof(int)); | 
|---|
| 139 |  | -	int i, error = 0; | 
|---|
|  | 139 | +	int nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | 
|---|
|  | 140 | +	struct bpf_link **links = calloc(nr_cpus, sizeof(struct bpf_link *)); | 
|---|
|  | 141 | +	int i, pmu_fd, error = 1; | 
|---|
|  | 142 | + | 
|---|
|  | 143 | +	if (!links) { | 
|---|
|  | 144 | +		printf("malloc of links failed\n"); | 
|---|
|  | 145 | +		goto err; | 
|---|
|  | 146 | +	} | 
|---|
| 140 | 147 |  | 
|---|
| 141 | 148 | /* system wide perf event, no need to inherit */ | 
|---|
| 142 | 149 | attr->inherit = 0; | 
|---|
| 143 | 150 |  | 
|---|
| 144 | 151 | /* open perf_event on all cpus */ | 
|---|
| 145 | 152 | for (i = 0; i < nr_cpus; i++) { | 
|---|
| 146 |  | -		pmu_fd[i] = sys_perf_event_open(attr, -1, i, -1, 0); | 
|---|
| 147 |  | -		if (pmu_fd[i] < 0) { | 
|---|
|  | 153 | +		pmu_fd = sys_perf_event_open(attr, -1, i, -1, 0); | 
|---|
|  | 154 | +		if (pmu_fd < 0) { | 
|---|
| 148 | 155 | printf("sys_perf_event_open failed\n"); | 
|---|
| 149 |  | -			error = 1; | 
|---|
| 150 | 156 | goto all_cpu_err; | 
|---|
| 151 | 157 | } | 
|---|
| 152 |  | -		assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, prog_fd[0]) == 0); | 
|---|
| 153 |  | -		assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE) == 0); | 
|---|
|  | 158 | +		links[i] = bpf_program__attach_perf_event(prog, pmu_fd); | 
|---|
|  | 159 | +		if (libbpf_get_error(links[i])) { | 
|---|
|  | 160 | +			printf("bpf_program__attach_perf_event failed\n"); | 
|---|
|  | 161 | +			links[i] = NULL; | 
|---|
|  | 162 | +			close(pmu_fd); | 
|---|
|  | 163 | +			goto all_cpu_err; | 
|---|
|  | 164 | +		} | 
|---|
| 154 | 165 | } | 
|---|
| 155 | 166 |  | 
|---|
| 156 |  | -	if (generate_load() < 0) { | 
|---|
| 157 |  | -		error = 1; | 
|---|
|  | 167 | +	if (generate_load() < 0) | 
|---|
| 158 | 168 | goto all_cpu_err; | 
|---|
| 159 |  | -	} | 
|---|
|  | 169 | + | 
|---|
| 160 | 170 | print_stacks(); | 
|---|
|  | 171 | +	error = 0; | 
|---|
| 161 | 172 | all_cpu_err: | 
|---|
| 162 |  | -	for (i--; i >= 0; i--) { | 
|---|
| 163 |  | -		ioctl(pmu_fd[i], PERF_EVENT_IOC_DISABLE); | 
|---|
| 164 |  | -		close(pmu_fd[i]); | 
|---|
| 165 |  | -	} | 
|---|
| 166 |  | -	free(pmu_fd); | 
|---|
|  | 173 | +	for (i--; i >= 0; i--) | 
|---|
|  | 174 | +		bpf_link__destroy(links[i]); | 
|---|
|  | 175 | +err: | 
|---|
|  | 176 | +	free(links); | 
|---|
| 167 | 177 | if (error) | 
|---|
| 168 |  | -		int_exit(0); | 
|---|
|  | 178 | +		err_exit(error); | 
|---|
| 169 | 179 | } | 
|---|
| 170 | 180 |  | 
|---|
| 171 | 181 | static void test_perf_event_task(struct perf_event_attr *attr) | 
|---|
| 172 | 182 | { | 
|---|
| 173 |  | -	int pmu_fd, error = 0; | 
|---|
|  | 183 | +	struct bpf_link *link = NULL; | 
|---|
|  | 184 | +	int pmu_fd, error = 1; | 
|---|
| 174 | 185 |  | 
|---|
| 175 | 186 | /* per task perf event, enable inherit so the "dd ..." command can be traced properly. | 
|---|
| 176 | 187 | * Enabling inherit will cause bpf_perf_prog_read_time helper failure. | 
|---|
| .. | .. | 
|---|
| 181 | 192 | pmu_fd = sys_perf_event_open(attr, 0, -1, -1, 0); | 
|---|
| 182 | 193 | if (pmu_fd < 0) { | 
|---|
| 183 | 194 | printf("sys_perf_event_open failed\n"); | 
|---|
| 184 |  | -		int_exit(0); | 
|---|
| 185 |  | -	} | 
|---|
| 186 |  | -	assert(ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd[0]) == 0); | 
|---|
| 187 |  | -	assert(ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE) == 0); | 
|---|
| 188 |  | - | 
|---|
| 189 |  | -	if (generate_load() < 0) { | 
|---|
| 190 |  | -		error = 1; | 
|---|
| 191 | 195 | goto err; | 
|---|
| 192 | 196 | } | 
|---|
|  | 197 | +	link = bpf_program__attach_perf_event(prog, pmu_fd); | 
|---|
|  | 198 | +	if (libbpf_get_error(link)) { | 
|---|
|  | 199 | +		printf("bpf_program__attach_perf_event failed\n"); | 
|---|
|  | 200 | +		link = NULL; | 
|---|
|  | 201 | +		close(pmu_fd); | 
|---|
|  | 202 | +		goto err; | 
|---|
|  | 203 | +	} | 
|---|
|  | 204 | + | 
|---|
|  | 205 | +	if (generate_load() < 0) | 
|---|
|  | 206 | +		goto err; | 
|---|
|  | 207 | + | 
|---|
| 193 | 208 | print_stacks(); | 
|---|
|  | 209 | +	error = 0; | 
|---|
| 194 | 210 | err: | 
|---|
| 195 |  | -	ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); | 
|---|
| 196 |  | -	close(pmu_fd); | 
|---|
|  | 211 | +	bpf_link__destroy(link); | 
|---|
| 197 | 212 | if (error) | 
|---|
| 198 |  | -		int_exit(0); | 
|---|
|  | 213 | +		err_exit(error); | 
|---|
| 199 | 214 | } | 
|---|
| 200 | 215 |  | 
|---|
| 201 | 216 | static void test_bpf_perf_event(void) | 
|---|
| .. | .. | 
|---|
| 280 | 295 | int main(int argc, char **argv) | 
|---|
| 281 | 296 | { | 
|---|
| 282 | 297 | struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; | 
|---|
|  | 298 | +	struct bpf_object *obj = NULL; | 
|---|
| 283 | 299 | char filename[256]; | 
|---|
|  | 300 | +	int error = 1; | 
|---|
| 284 | 301 |  | 
|---|
| 285 | 302 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); | 
|---|
| 286 | 303 | setrlimit(RLIMIT_MEMLOCK, &r); | 
|---|
| 287 | 304 |  | 
|---|
| 288 |  | -	signal(SIGINT, int_exit); | 
|---|
| 289 |  | -	signal(SIGTERM, int_exit); | 
|---|
|  | 305 | +	signal(SIGINT, err_exit); | 
|---|
|  | 306 | +	signal(SIGTERM, err_exit); | 
|---|
| 290 | 307 |  | 
|---|
| 291 | 308 | if (load_kallsyms()) { | 
|---|
| 292 | 309 | printf("failed to process /proc/kallsyms\n"); | 
|---|
| 293 |  | -		return 1; | 
|---|
|  | 310 | +		goto cleanup; | 
|---|
| 294 | 311 | } | 
|---|
| 295 | 312 |  | 
|---|
| 296 |  | -	if (load_bpf_file(filename)) { | 
|---|
| 297 |  | -		printf("%s", bpf_log_buf); | 
|---|
| 298 |  | -		return 2; | 
|---|
|  | 313 | +	obj = bpf_object__open_file(filename, NULL); | 
|---|
|  | 314 | +	if (libbpf_get_error(obj)) { | 
|---|
|  | 315 | +		printf("opening BPF object file failed\n"); | 
|---|
|  | 316 | +		obj = NULL; | 
|---|
|  | 317 | +		goto cleanup; | 
|---|
| 299 | 318 | } | 
|---|
| 300 | 319 |  | 
|---|
| 301 |  | -	if (fork() == 0) { | 
|---|
|  | 320 | +	prog = bpf_object__find_program_by_name(obj, "bpf_prog1"); | 
|---|
|  | 321 | +	if (!prog) { | 
|---|
|  | 322 | +		printf("finding a prog in obj file failed\n"); | 
|---|
|  | 323 | +		goto cleanup; | 
|---|
|  | 324 | +	} | 
|---|
|  | 325 | + | 
|---|
|  | 326 | +	/* load BPF program */ | 
|---|
|  | 327 | +	if (bpf_object__load(obj)) { | 
|---|
|  | 328 | +		printf("loading BPF object file failed\n"); | 
|---|
|  | 329 | +		goto cleanup; | 
|---|
|  | 330 | +	} | 
|---|
|  | 331 | + | 
|---|
|  | 332 | +	map_fd[0] = bpf_object__find_map_fd_by_name(obj, "counts"); | 
|---|
|  | 333 | +	map_fd[1] = bpf_object__find_map_fd_by_name(obj, "stackmap"); | 
|---|
|  | 334 | +	if (map_fd[0] < 0 || map_fd[1] < 0) { | 
|---|
|  | 335 | +		printf("finding a counts/stackmap map in obj file failed\n"); | 
|---|
|  | 336 | +		goto cleanup; | 
|---|
|  | 337 | +	} | 
|---|
|  | 338 | + | 
|---|
|  | 339 | +	pid = fork(); | 
|---|
|  | 340 | +	if (pid == 0) { | 
|---|
| 302 | 341 | read_trace_pipe(); | 
|---|
| 303 | 342 | return 0; | 
|---|
|  | 343 | +	} else if (pid == -1) { | 
|---|
|  | 344 | +		printf("couldn't spawn process\n"); | 
|---|
|  | 345 | +		goto cleanup; | 
|---|
| 304 | 346 | } | 
|---|
|  | 347 | + | 
|---|
| 305 | 348 | test_bpf_perf_event(); | 
|---|
| 306 |  | -	int_exit(0); | 
|---|
| 307 |  | -	return 0; | 
|---|
|  | 349 | +	error = 0; | 
|---|
|  | 350 | + | 
|---|
|  | 351 | +cleanup: | 
|---|
|  | 352 | +	bpf_object__close(obj); | 
|---|
|  | 353 | +	err_exit(error); | 
|---|
| 308 | 354 | } | 
|---|