123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- // SPDX-License-Identifier: GPL-2.0+
- // Copyright (C) 2018 Facebook
- // Author: Yonghong Song <yhs@fb.com>
- #define _GNU_SOURCE
- #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 <ftw.h>
- #include <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;
- }
- 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;
- }
- }
- static int show_proc(const char *fpath, const struct stat *sb,
- int tflag, struct FTW *ftwbuf)
- {
- __u64 probe_offset, probe_addr;
- __u32 len, prog_id, fd_type;
- int err, pid = 0, fd = 0;
- const char *pch;
- char buf[4096];
- /* prefix always /proc */
- pch = fpath + 5;
- if (*pch == '\0')
- return 0;
- /* pid should be all numbers */
- pch++;
- while (isdigit(*pch)) {
- pid = pid * 10 + *pch - '0';
- pch++;
- }
- if (*pch == '\0')
- return 0;
- if (*pch != '/')
- return FTW_SKIP_SUBTREE;
- /* check /proc/<pid>/fd directory */
- pch++;
- if (strncmp(pch, "fd", 2))
- return FTW_SKIP_SUBTREE;
- pch += 2;
- if (*pch == '\0')
- return 0;
- if (*pch != '/')
- return FTW_SKIP_SUBTREE;
- /* check /proc/<pid>/fd/<fd_num> */
- pch++;
- while (isdigit(*pch)) {
- fd = fd * 10 + *pch - '0';
- pch++;
- }
- if (*pch != '\0')
- return FTW_SKIP_SUBTREE;
- /* 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)
- return 0;
- 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);
- return 0;
- }
- static int do_show(int argc, char **argv)
- {
- int flags = FTW_ACTIONRETVAL | FTW_PHYS;
- int err = 0, nopenfd = 16;
- if (!has_perf_query_support())
- return -1;
- if (json_output)
- jsonw_start_array(json_wtr);
- if (nftw("/proc", show_proc, nopenfd, flags) == -1) {
- p_err("%s", strerror(errno));
- err = -1;
- }
- if (json_output)
- jsonw_end_array(json_wtr);
- return err;
- }
- static int do_help(int argc, char **argv)
- {
- fprintf(stderr,
- "Usage: %s %s { show | list | help }\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);
- }
|