424 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <stdio.h>4#include <stdlib.h>5#include <signal.h>6#include <unistd.h>7#include <stdbool.h>8#include <string.h>9#include <stdint.h>10#include <fcntl.h>11#include <linux/bpf.h>12#include <sys/ioctl.h>13#include <sys/types.h>14#include <sys/stat.h>15#include <linux/perf_event.h>16 17#include <bpf/bpf.h>18#include <bpf/libbpf.h>19#include "bpf_util.h"20#include "perf-sys.h"21#include "trace_helpers.h"22 23static struct bpf_program *progs[2];24static struct bpf_link *links[2];25 26#define CHECK_PERROR_RET(condition) ({ \27 int __ret = !!(condition); \28 if (__ret) { \29 printf("FAIL: %s:\n", __func__); \30 perror(" "); \31 return -1; \32 } \33})34 35#define CHECK_AND_RET(condition) ({ \36 int __ret = !!(condition); \37 if (__ret) \38 return -1; \39})40 41static __u64 ptr_to_u64(void *ptr)42{43 return (__u64) (unsigned long) ptr;44}45 46#define PMU_TYPE_FILE "/sys/bus/event_source/devices/%s/type"47static int bpf_find_probe_type(const char *event_type)48{49 char buf[256];50 int fd, ret;51 52 ret = snprintf(buf, sizeof(buf), PMU_TYPE_FILE, event_type);53 CHECK_PERROR_RET(ret < 0 || ret >= sizeof(buf));54 55 fd = open(buf, O_RDONLY);56 CHECK_PERROR_RET(fd < 0);57 58 ret = read(fd, buf, sizeof(buf));59 close(fd);60 CHECK_PERROR_RET(ret < 0 || ret >= sizeof(buf));61 62 errno = 0;63 ret = (int)strtol(buf, NULL, 10);64 CHECK_PERROR_RET(errno);65 return ret;66}67 68#define PMU_RETPROBE_FILE "/sys/bus/event_source/devices/%s/format/retprobe"69static int bpf_get_retprobe_bit(const char *event_type)70{71 char buf[256];72 int fd, ret;73 74 ret = snprintf(buf, sizeof(buf), PMU_RETPROBE_FILE, event_type);75 CHECK_PERROR_RET(ret < 0 || ret >= sizeof(buf));76 77 fd = open(buf, O_RDONLY);78 CHECK_PERROR_RET(fd < 0);79 80 ret = read(fd, buf, sizeof(buf));81 close(fd);82 CHECK_PERROR_RET(ret < 0 || ret >= sizeof(buf));83 CHECK_PERROR_RET(strlen(buf) < strlen("config:"));84 85 errno = 0;86 ret = (int)strtol(buf + strlen("config:"), NULL, 10);87 CHECK_PERROR_RET(errno);88 return ret;89}90 91static int test_debug_fs_kprobe(int link_idx, const char *fn_name,92 __u32 expected_fd_type)93{94 __u64 probe_offset, probe_addr;95 __u32 len, prog_id, fd_type;96 int err, event_fd;97 char buf[256];98 99 len = sizeof(buf);100 event_fd = bpf_link__fd(links[link_idx]);101 err = bpf_task_fd_query(getpid(), event_fd, 0, buf, &len,102 &prog_id, &fd_type, &probe_offset,103 &probe_addr);104 if (err < 0) {105 printf("FAIL: %s, for event_fd idx %d, fn_name %s\n",106 __func__, link_idx, fn_name);107 perror(" :");108 return -1;109 }110 if (strcmp(buf, fn_name) != 0 ||111 fd_type != expected_fd_type ||112 probe_offset != 0x0 || probe_addr != 0x0) {113 printf("FAIL: bpf_trace_event_query(event_fd[%d]):\n",114 link_idx);115 printf("buf: %s, fd_type: %u, probe_offset: 0x%llx,"116 " probe_addr: 0x%llx\n",117 buf, fd_type, probe_offset, probe_addr);118 return -1;119 }120 return 0;121}122 123static int test_nondebug_fs_kuprobe_common(const char *event_type,124 const char *name, __u64 offset, __u64 addr, bool is_return,125 char *buf, __u32 *buf_len, __u32 *prog_id, __u32 *fd_type,126 __u64 *probe_offset, __u64 *probe_addr)127{128 int is_return_bit = bpf_get_retprobe_bit(event_type);129 int type = bpf_find_probe_type(event_type);130 struct perf_event_attr attr = {};131 struct bpf_link *link;132 int fd, err = -1;133 134 if (type < 0 || is_return_bit < 0) {135 printf("FAIL: %s incorrect type (%d) or is_return_bit (%d)\n",136 __func__, type, is_return_bit);137 return err;138 }139 140 attr.sample_period = 1;141 attr.wakeup_events = 1;142 if (is_return)143 attr.config |= 1 << is_return_bit;144 145 if (name) {146 attr.config1 = ptr_to_u64((void *)name);147 attr.config2 = offset;148 } else {149 attr.config1 = 0;150 attr.config2 = addr;151 }152 attr.size = sizeof(attr);153 attr.type = type;154 155 fd = sys_perf_event_open(&attr, -1, 0, -1, 0);156 link = bpf_program__attach_perf_event(progs[0], fd);157 if (libbpf_get_error(link)) {158 printf("ERROR: bpf_program__attach_perf_event failed\n");159 link = NULL;160 close(fd);161 goto cleanup;162 }163 164 CHECK_PERROR_RET(bpf_task_fd_query(getpid(), fd, 0, buf, buf_len,165 prog_id, fd_type, probe_offset, probe_addr) < 0);166 err = 0;167 168cleanup:169 bpf_link__destroy(link);170 return err;171}172 173static int test_nondebug_fs_probe(const char *event_type, const char *name,174 __u64 offset, __u64 addr, bool is_return,175 __u32 expected_fd_type,176 __u32 expected_ret_fd_type,177 char *buf, __u32 buf_len)178{179 __u64 probe_offset, probe_addr;180 __u32 prog_id, fd_type;181 int err;182 183 err = test_nondebug_fs_kuprobe_common(event_type, name,184 offset, addr, is_return,185 buf, &buf_len, &prog_id,186 &fd_type, &probe_offset,187 &probe_addr);188 if (err < 0) {189 printf("FAIL: %s, "190 "for name %s, offset 0x%llx, addr 0x%llx, is_return %d\n",191 __func__, name ? name : "", offset, addr, is_return);192 perror(" :");193 return -1;194 }195 if ((is_return && fd_type != expected_ret_fd_type) ||196 (!is_return && fd_type != expected_fd_type)) {197 printf("FAIL: %s, incorrect fd_type %u\n",198 __func__, fd_type);199 return -1;200 }201 if (name) {202 if (strcmp(name, buf) != 0) {203 printf("FAIL: %s, incorrect buf %s\n", __func__, buf);204 return -1;205 }206 if (probe_offset != offset) {207 printf("FAIL: %s, incorrect probe_offset 0x%llx\n",208 __func__, probe_offset);209 return -1;210 }211 } else {212 if (buf_len != 0) {213 printf("FAIL: %s, incorrect buf %p\n",214 __func__, buf);215 return -1;216 }217 218 if (probe_addr != addr) {219 printf("FAIL: %s, incorrect probe_addr 0x%llx\n",220 __func__, probe_addr);221 return -1;222 }223 }224 return 0;225}226 227static int test_debug_fs_uprobe(char *binary_path, long offset, bool is_return)228{229 char buf[256], event_alias[sizeof("test_1234567890")];230 const char *event_type = "uprobe";231 struct perf_event_attr attr = {};232 __u64 probe_offset, probe_addr;233 __u32 len, prog_id, fd_type;234 int err = -1, res, kfd, efd;235 struct bpf_link *link;236 ssize_t bytes;237 238 snprintf(buf, sizeof(buf), "/sys/kernel/tracing/%s_events",239 event_type);240 kfd = open(buf, O_WRONLY | O_TRUNC, 0);241 CHECK_PERROR_RET(kfd < 0);242 243 res = snprintf(event_alias, sizeof(event_alias), "test_%d", getpid());244 CHECK_PERROR_RET(res < 0 || res >= sizeof(event_alias));245 246 res = snprintf(buf, sizeof(buf), "%c:%ss/%s %s:0x%lx",247 is_return ? 'r' : 'p', event_type, event_alias,248 binary_path, offset);249 CHECK_PERROR_RET(res < 0 || res >= sizeof(buf));250 CHECK_PERROR_RET(write(kfd, buf, strlen(buf)) < 0);251 252 close(kfd);253 kfd = -1;254 255 snprintf(buf, sizeof(buf), "/sys/kernel/tracing/events/%ss/%s/id",256 event_type, event_alias);257 efd = open(buf, O_RDONLY, 0);258 CHECK_PERROR_RET(efd < 0);259 260 bytes = read(efd, buf, sizeof(buf));261 CHECK_PERROR_RET(bytes <= 0 || bytes >= sizeof(buf));262 close(efd);263 buf[bytes] = '\0';264 265 attr.config = strtol(buf, NULL, 0);266 attr.type = PERF_TYPE_TRACEPOINT;267 attr.sample_period = 1;268 attr.wakeup_events = 1;269 270 kfd = sys_perf_event_open(&attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);271 link = bpf_program__attach_perf_event(progs[0], kfd);272 if (libbpf_get_error(link)) {273 printf("ERROR: bpf_program__attach_perf_event failed\n");274 link = NULL;275 close(kfd);276 goto cleanup;277 }278 279 len = sizeof(buf);280 err = bpf_task_fd_query(getpid(), kfd, 0, buf, &len,281 &prog_id, &fd_type, &probe_offset,282 &probe_addr);283 if (err < 0) {284 printf("FAIL: %s, binary_path %s\n", __func__, binary_path);285 perror(" :");286 return -1;287 }288 if ((is_return && fd_type != BPF_FD_TYPE_URETPROBE) ||289 (!is_return && fd_type != BPF_FD_TYPE_UPROBE)) {290 printf("FAIL: %s, incorrect fd_type %u\n", __func__,291 fd_type);292 return -1;293 }294 if (strcmp(binary_path, buf) != 0) {295 printf("FAIL: %s, incorrect buf %s\n", __func__, buf);296 return -1;297 }298 if (probe_offset != offset) {299 printf("FAIL: %s, incorrect probe_offset 0x%llx\n", __func__,300 probe_offset);301 return -1;302 }303 err = 0;304 305cleanup:306 bpf_link__destroy(link);307 return err;308}309 310int main(int argc, char **argv)311{312 extern char __executable_start;313 char filename[256], buf[256];314 __u64 uprobe_file_offset;315 struct bpf_program *prog;316 struct bpf_object *obj;317 int i = 0, err = -1;318 319 if (load_kallsyms()) {320 printf("failed to process /proc/kallsyms\n");321 return err;322 }323 324 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);325 obj = bpf_object__open_file(filename, NULL);326 if (libbpf_get_error(obj)) {327 fprintf(stderr, "ERROR: opening BPF object file failed\n");328 return err;329 }330 331 /* load BPF program */332 if (bpf_object__load(obj)) {333 fprintf(stderr, "ERROR: loading BPF object file failed\n");334 goto cleanup;335 }336 337 bpf_object__for_each_program(prog, obj) {338 progs[i] = prog;339 links[i] = bpf_program__attach(progs[i]);340 if (libbpf_get_error(links[i])) {341 fprintf(stderr, "ERROR: bpf_program__attach failed\n");342 links[i] = NULL;343 goto cleanup;344 }345 i++;346 }347 348 /* test two functions in the corresponding *_kern.c file */349 CHECK_AND_RET(test_debug_fs_kprobe(0, "blk_mq_start_request",350 BPF_FD_TYPE_KPROBE));351 CHECK_AND_RET(test_debug_fs_kprobe(1, "__blk_account_io_done",352 BPF_FD_TYPE_KRETPROBE));353 354 /* test nondebug fs kprobe */355 CHECK_AND_RET(test_nondebug_fs_probe("kprobe", "bpf_check", 0x0, 0x0,356 false, BPF_FD_TYPE_KPROBE,357 BPF_FD_TYPE_KRETPROBE,358 buf, sizeof(buf)));359#ifdef __x86_64__360 /* set a kprobe on "bpf_check + 0x5", which is x64 specific */361 CHECK_AND_RET(test_nondebug_fs_probe("kprobe", "bpf_check", 0x5, 0x0,362 false, BPF_FD_TYPE_KPROBE,363 BPF_FD_TYPE_KRETPROBE,364 buf, sizeof(buf)));365#endif366 CHECK_AND_RET(test_nondebug_fs_probe("kprobe", "bpf_check", 0x0, 0x0,367 true, BPF_FD_TYPE_KPROBE,368 BPF_FD_TYPE_KRETPROBE,369 buf, sizeof(buf)));370 CHECK_AND_RET(test_nondebug_fs_probe("kprobe", NULL, 0x0,371 ksym_get_addr("bpf_check"), false,372 BPF_FD_TYPE_KPROBE,373 BPF_FD_TYPE_KRETPROBE,374 buf, sizeof(buf)));375 CHECK_AND_RET(test_nondebug_fs_probe("kprobe", NULL, 0x0,376 ksym_get_addr("bpf_check"), false,377 BPF_FD_TYPE_KPROBE,378 BPF_FD_TYPE_KRETPROBE,379 NULL, 0));380 CHECK_AND_RET(test_nondebug_fs_probe("kprobe", NULL, 0x0,381 ksym_get_addr("bpf_check"), true,382 BPF_FD_TYPE_KPROBE,383 BPF_FD_TYPE_KRETPROBE,384 buf, sizeof(buf)));385 CHECK_AND_RET(test_nondebug_fs_probe("kprobe", NULL, 0x0,386 ksym_get_addr("bpf_check"), true,387 BPF_FD_TYPE_KPROBE,388 BPF_FD_TYPE_KRETPROBE,389 0, 0));390 391 /* test nondebug fs uprobe */392 /* the calculation of uprobe file offset is based on gcc 7.3.1 on x64393 * and the default linker script, which defines __executable_start as394 * the start of the .text section. The calculation could be different395 * on different systems with different compilers. The right way is396 * to parse the ELF file. We took a shortcut here.397 */398 uprobe_file_offset = (unsigned long)main - (unsigned long)&__executable_start;399 CHECK_AND_RET(test_nondebug_fs_probe("uprobe", (char *)argv[0],400 uprobe_file_offset, 0x0, false,401 BPF_FD_TYPE_UPROBE,402 BPF_FD_TYPE_URETPROBE,403 buf, sizeof(buf)));404 CHECK_AND_RET(test_nondebug_fs_probe("uprobe", (char *)argv[0],405 uprobe_file_offset, 0x0, true,406 BPF_FD_TYPE_UPROBE,407 BPF_FD_TYPE_URETPROBE,408 buf, sizeof(buf)));409 410 /* test debug fs uprobe */411 CHECK_AND_RET(test_debug_fs_uprobe((char *)argv[0], uprobe_file_offset,412 false));413 CHECK_AND_RET(test_debug_fs_uprobe((char *)argv[0], uprobe_file_offset,414 true));415 err = 0;416 417cleanup:418 for (i--; i >= 0; i--)419 bpf_link__destroy(links[i]);420 421 bpf_object__close(obj);422 return err;423}424