linux/tools/bpf/bpftool/perf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2// Copyright (C) 2018 Facebook
   3// Author: Yonghong Song <yhs@fb.com>
   4
   5#define _GNU_SOURCE
   6#include <ctype.h>
   7#include <errno.h>
   8#include <fcntl.h>
   9#include <stdlib.h>
  10#include <string.h>
  11#include <sys/stat.h>
  12#include <sys/types.h>
  13#include <unistd.h>
  14#include <ftw.h>
  15
  16#include <bpf/bpf.h>
  17
  18#include "main.h"
  19
  20/* 0: undecided, 1: supported, 2: not supported */
  21static int perf_query_supported;
  22static bool has_perf_query_support(void)
  23{
  24        __u64 probe_offset, probe_addr;
  25        __u32 len, prog_id, fd_type;
  26        char buf[256];
  27        int fd;
  28
  29        if (perf_query_supported)
  30                goto out;
  31
  32        fd = open("/", O_RDONLY);
  33        if (fd < 0) {
  34                p_err("perf_query_support: cannot open directory \"/\" (%s)",
  35                      strerror(errno));
  36                goto out;
  37        }
  38
  39        /* the following query will fail as no bpf attachment,
  40         * the expected errno is ENOTSUPP
  41         */
  42        errno = 0;
  43        len = sizeof(buf);
  44        bpf_task_fd_query(getpid(), fd, 0, buf, &len, &prog_id,
  45                          &fd_type, &probe_offset, &probe_addr);
  46
  47        if (errno == 524 /* ENOTSUPP */) {
  48                perf_query_supported = 1;
  49                goto close_fd;
  50        }
  51
  52        perf_query_supported = 2;
  53        p_err("perf_query_support: %s", strerror(errno));
  54        fprintf(stderr,
  55                "HINT: non root or kernel doesn't support TASK_FD_QUERY\n");
  56
  57close_fd:
  58        close(fd);
  59out:
  60        return perf_query_supported == 1;
  61}
  62
  63static void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type,
  64                            char *buf, __u64 probe_offset, __u64 probe_addr)
  65{
  66        jsonw_start_object(json_wtr);
  67        jsonw_int_field(json_wtr, "pid", pid);
  68        jsonw_int_field(json_wtr, "fd", fd);
  69        jsonw_uint_field(json_wtr, "prog_id", prog_id);
  70        switch (fd_type) {
  71        case BPF_FD_TYPE_RAW_TRACEPOINT:
  72                jsonw_string_field(json_wtr, "fd_type", "raw_tracepoint");
  73                jsonw_string_field(json_wtr, "tracepoint", buf);
  74                break;
  75        case BPF_FD_TYPE_TRACEPOINT:
  76                jsonw_string_field(json_wtr, "fd_type", "tracepoint");
  77                jsonw_string_field(json_wtr, "tracepoint", buf);
  78                break;
  79        case BPF_FD_TYPE_KPROBE:
  80                jsonw_string_field(json_wtr, "fd_type", "kprobe");
  81                if (buf[0] != '\0') {
  82                        jsonw_string_field(json_wtr, "func", buf);
  83                        jsonw_lluint_field(json_wtr, "offset", probe_offset);
  84                } else {
  85                        jsonw_lluint_field(json_wtr, "addr", probe_addr);
  86                }
  87                break;
  88        case BPF_FD_TYPE_KRETPROBE:
  89                jsonw_string_field(json_wtr, "fd_type", "kretprobe");
  90                if (buf[0] != '\0') {
  91                        jsonw_string_field(json_wtr, "func", buf);
  92                        jsonw_lluint_field(json_wtr, "offset", probe_offset);
  93                } else {
  94                        jsonw_lluint_field(json_wtr, "addr", probe_addr);
  95                }
  96                break;
  97        case BPF_FD_TYPE_UPROBE:
  98                jsonw_string_field(json_wtr, "fd_type", "uprobe");
  99                jsonw_string_field(json_wtr, "filename", buf);
 100                jsonw_lluint_field(json_wtr, "offset", probe_offset);
 101                break;
 102        case BPF_FD_TYPE_URETPROBE:
 103                jsonw_string_field(json_wtr, "fd_type", "uretprobe");
 104                jsonw_string_field(json_wtr, "filename", buf);
 105                jsonw_lluint_field(json_wtr, "offset", probe_offset);
 106                break;
 107        default:
 108                break;
 109        }
 110        jsonw_end_object(json_wtr);
 111}
 112
 113static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
 114                             char *buf, __u64 probe_offset, __u64 probe_addr)
 115{
 116        printf("pid %d  fd %d: prog_id %u  ", pid, fd, prog_id);
 117        switch (fd_type) {
 118        case BPF_FD_TYPE_RAW_TRACEPOINT:
 119                printf("raw_tracepoint  %s\n", buf);
 120                break;
 121        case BPF_FD_TYPE_TRACEPOINT:
 122                printf("tracepoint  %s\n", buf);
 123                break;
 124        case BPF_FD_TYPE_KPROBE:
 125                if (buf[0] != '\0')
 126                        printf("kprobe  func %s  offset %llu\n", buf,
 127                               probe_offset);
 128                else
 129                        printf("kprobe  addr %llu\n", probe_addr);
 130                break;
 131        case BPF_FD_TYPE_KRETPROBE:
 132                if (buf[0] != '\0')
 133                        printf("kretprobe  func %s  offset %llu\n", buf,
 134                               probe_offset);
 135                else
 136                        printf("kretprobe  addr %llu\n", probe_addr);
 137                break;
 138        case BPF_FD_TYPE_UPROBE:
 139                printf("uprobe  filename %s  offset %llu\n", buf, probe_offset);
 140                break;
 141        case BPF_FD_TYPE_URETPROBE:
 142                printf("uretprobe  filename %s  offset %llu\n", buf,
 143                       probe_offset);
 144                break;
 145        default:
 146                break;
 147        }
 148}
 149
 150static int show_proc(const char *fpath, const struct stat *sb,
 151                     int tflag, struct FTW *ftwbuf)
 152{
 153        __u64 probe_offset, probe_addr;
 154        __u32 len, prog_id, fd_type;
 155        int err, pid = 0, fd = 0;
 156        const char *pch;
 157        char buf[4096];
 158
 159        /* prefix always /proc */
 160        pch = fpath + 5;
 161        if (*pch == '\0')
 162                return 0;
 163
 164        /* pid should be all numbers */
 165        pch++;
 166        while (isdigit(*pch)) {
 167                pid = pid * 10 + *pch - '0';
 168                pch++;
 169        }
 170        if (*pch == '\0')
 171                return 0;
 172        if (*pch != '/')
 173                return FTW_SKIP_SUBTREE;
 174
 175        /* check /proc/<pid>/fd directory */
 176        pch++;
 177        if (strncmp(pch, "fd", 2))
 178                return FTW_SKIP_SUBTREE;
 179        pch += 2;
 180        if (*pch == '\0')
 181                return 0;
 182        if (*pch != '/')
 183                return FTW_SKIP_SUBTREE;
 184
 185        /* check /proc/<pid>/fd/<fd_num> */
 186        pch++;
 187        while (isdigit(*pch)) {
 188                fd = fd * 10 + *pch - '0';
 189                pch++;
 190        }
 191        if (*pch != '\0')
 192                return FTW_SKIP_SUBTREE;
 193
 194        /* query (pid, fd) for potential perf events */
 195        len = sizeof(buf);
 196        err = bpf_task_fd_query(pid, fd, 0, buf, &len, &prog_id, &fd_type,
 197                                &probe_offset, &probe_addr);
 198        if (err < 0)
 199                return 0;
 200
 201        if (json_output)
 202                print_perf_json(pid, fd, prog_id, fd_type, buf, probe_offset,
 203                                probe_addr);
 204        else
 205                print_perf_plain(pid, fd, prog_id, fd_type, buf, probe_offset,
 206                                 probe_addr);
 207
 208        return 0;
 209}
 210
 211static int do_show(int argc, char **argv)
 212{
 213        int flags = FTW_ACTIONRETVAL | FTW_PHYS;
 214        int err = 0, nopenfd = 16;
 215
 216        if (!has_perf_query_support())
 217                return -1;
 218
 219        if (json_output)
 220                jsonw_start_array(json_wtr);
 221        if (nftw("/proc", show_proc, nopenfd, flags) == -1) {
 222                p_err("%s", strerror(errno));
 223                err = -1;
 224        }
 225        if (json_output)
 226                jsonw_end_array(json_wtr);
 227
 228        return err;
 229}
 230
 231static int do_help(int argc, char **argv)
 232{
 233        fprintf(stderr,
 234                "Usage: %1$s %2$s { show | list }\n"
 235                "       %1$s %2$s help }\n"
 236                "\n"
 237                "       " HELP_SPEC_OPTIONS " }\n"
 238                "",
 239                bin_name, argv[-2]);
 240
 241        return 0;
 242}
 243
 244static const struct cmd cmds[] = {
 245        { "show",       do_show },
 246        { "list",       do_show },
 247        { "help",       do_help },
 248        { 0 }
 249};
 250
 251int do_perf(int argc, char **argv)
 252{
 253        return cmd_select(cmds, argc, argv, do_help);
 254}
 255