diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
commit | ace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch) | |
tree | b2d64bc10158fdd5497876388cd68142ca374ed3 /tools/bpf/bpftool/perf.c | |
parent | Initial commit. (diff) | |
download | linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip |
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/bpf/bpftool/perf.c')
-rw-r--r-- | tools/bpf/bpftool/perf.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/tools/bpf/bpftool/perf.c b/tools/bpf/bpftool/perf.c new file mode 100644 index 0000000000..80de2874da --- /dev/null +++ b/tools/bpf/bpftool/perf.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +// Copyright (C) 2018 Facebook +// Author: Yonghong Song <yhs@fb.com> + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <dirent.h> + +#include <bpf/bpf.h> + +#include "main.h" + +/* 0: undecided, 1: supported, 2: not supported */ +static int perf_query_supported; +static bool has_perf_query_support(void) +{ + __u64 probe_offset, probe_addr; + __u32 len, prog_id, fd_type; + char buf[256]; + int fd; + + if (perf_query_supported) + goto out; + + fd = open("/", O_RDONLY); + if (fd < 0) { + p_err("perf_query_support: cannot open directory \"/\" (%s)", + strerror(errno)); + goto out; + } + + /* the following query will fail as no bpf attachment, + * the expected errno is ENOTSUPP + */ + errno = 0; + len = sizeof(buf); + bpf_task_fd_query(getpid(), fd, 0, buf, &len, &prog_id, + &fd_type, &probe_offset, &probe_addr); + + if (errno == 524 /* ENOTSUPP */) { + perf_query_supported = 1; + goto close_fd; + } + + perf_query_supported = 2; + p_err("perf_query_support: %s", strerror(errno)); + fprintf(stderr, + "HINT: non root or kernel doesn't support TASK_FD_QUERY\n"); + +close_fd: + close(fd); +out: + return perf_query_supported == 1; +} + +static void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type, + char *buf, __u64 probe_offset, __u64 probe_addr) +{ + jsonw_start_object(json_wtr); + jsonw_int_field(json_wtr, "pid", pid); + jsonw_int_field(json_wtr, "fd", fd); + jsonw_uint_field(json_wtr, "prog_id", prog_id); + switch (fd_type) { + case BPF_FD_TYPE_RAW_TRACEPOINT: + jsonw_string_field(json_wtr, "fd_type", "raw_tracepoint"); + jsonw_string_field(json_wtr, "tracepoint", buf); + break; + case BPF_FD_TYPE_TRACEPOINT: + jsonw_string_field(json_wtr, "fd_type", "tracepoint"); + jsonw_string_field(json_wtr, "tracepoint", buf); + break; + case BPF_FD_TYPE_KPROBE: + jsonw_string_field(json_wtr, "fd_type", "kprobe"); + if (buf[0] != '\0') { + jsonw_string_field(json_wtr, "func", buf); + jsonw_lluint_field(json_wtr, "offset", probe_offset); + } else { + jsonw_lluint_field(json_wtr, "addr", probe_addr); + } + break; + case BPF_FD_TYPE_KRETPROBE: + jsonw_string_field(json_wtr, "fd_type", "kretprobe"); + if (buf[0] != '\0') { + jsonw_string_field(json_wtr, "func", buf); + jsonw_lluint_field(json_wtr, "offset", probe_offset); + } else { + jsonw_lluint_field(json_wtr, "addr", probe_addr); + } + break; + case BPF_FD_TYPE_UPROBE: + jsonw_string_field(json_wtr, "fd_type", "uprobe"); + jsonw_string_field(json_wtr, "filename", buf); + jsonw_lluint_field(json_wtr, "offset", probe_offset); + break; + case BPF_FD_TYPE_URETPROBE: + jsonw_string_field(json_wtr, "fd_type", "uretprobe"); + jsonw_string_field(json_wtr, "filename", buf); + jsonw_lluint_field(json_wtr, "offset", probe_offset); + break; + default: + break; + } + jsonw_end_object(json_wtr); +} + +static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type, + char *buf, __u64 probe_offset, __u64 probe_addr) +{ + printf("pid %d fd %d: prog_id %u ", pid, fd, prog_id); + switch (fd_type) { + case BPF_FD_TYPE_RAW_TRACEPOINT: + printf("raw_tracepoint %s\n", buf); + break; + case BPF_FD_TYPE_TRACEPOINT: + printf("tracepoint %s\n", buf); + break; + case BPF_FD_TYPE_KPROBE: + if (buf[0] != '\0') + printf("kprobe func %s offset %llu\n", buf, + probe_offset); + else + printf("kprobe addr %llu\n", probe_addr); + break; + case BPF_FD_TYPE_KRETPROBE: + if (buf[0] != '\0') + printf("kretprobe func %s offset %llu\n", buf, + probe_offset); + else + printf("kretprobe addr %llu\n", probe_addr); + break; + case BPF_FD_TYPE_UPROBE: + printf("uprobe filename %s offset %llu\n", buf, probe_offset); + break; + case BPF_FD_TYPE_URETPROBE: + printf("uretprobe filename %s offset %llu\n", buf, + probe_offset); + break; + default: + break; + } +} + +static int show_proc(void) +{ + struct dirent *proc_de, *pid_fd_de; + __u64 probe_offset, probe_addr; + __u32 len, prog_id, fd_type; + DIR *proc, *pid_fd; + int err, pid, fd; + const char *pch; + char buf[4096]; + + proc = opendir("/proc"); + if (!proc) + return -1; + + while ((proc_de = readdir(proc))) { + pid = 0; + pch = proc_de->d_name; + + /* pid should be all numbers */ + while (isdigit(*pch)) { + pid = pid * 10 + *pch - '0'; + pch++; + } + if (*pch != '\0') + continue; + + err = snprintf(buf, sizeof(buf), "/proc/%s/fd", proc_de->d_name); + if (err < 0 || err >= (int)sizeof(buf)) + continue; + + pid_fd = opendir(buf); + if (!pid_fd) + continue; + + while ((pid_fd_de = readdir(pid_fd))) { + fd = 0; + pch = pid_fd_de->d_name; + + /* fd should be all numbers */ + while (isdigit(*pch)) { + fd = fd * 10 + *pch - '0'; + pch++; + } + if (*pch != '\0') + continue; + + /* query (pid, fd) for potential perf events */ + len = sizeof(buf); + err = bpf_task_fd_query(pid, fd, 0, buf, &len, + &prog_id, &fd_type, + &probe_offset, &probe_addr); + if (err < 0) + continue; + + if (json_output) + print_perf_json(pid, fd, prog_id, fd_type, buf, + probe_offset, probe_addr); + else + print_perf_plain(pid, fd, prog_id, fd_type, buf, + probe_offset, probe_addr); + } + closedir(pid_fd); + } + closedir(proc); + return 0; +} + +static int do_show(int argc, char **argv) +{ + int err; + + if (!has_perf_query_support()) + return -1; + + if (json_output) + jsonw_start_array(json_wtr); + err = show_proc(); + if (json_output) + jsonw_end_array(json_wtr); + + return err; +} + +static int do_help(int argc, char **argv) +{ + fprintf(stderr, + "Usage: %1$s %2$s { show | list }\n" + " %1$s %2$s help\n" + "\n" + " " HELP_SPEC_OPTIONS " }\n" + "", + bin_name, argv[-2]); + + return 0; +} + +static const struct cmd cmds[] = { + { "show", do_show }, + { "list", do_show }, + { "help", do_help }, + { 0 } +}; + +int do_perf(int argc, char **argv) +{ + return cmd_select(cmds, argc, argv, do_help); +} |