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.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        }
 108        jsonw_end_object(json_wtr);
 109}
 110
 111static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
 112                             char *buf, __u64 probe_offset, __u64 probe_addr)
 113{
 114        printf("pid %d  fd %d: prog_id %u  ", pid, fd, prog_id);
 115        switch (fd_type) {
 116        case BPF_FD_TYPE_RAW_TRACEPOINT:
 117                printf("raw_tracepoint  %s\n", buf);
 118                break;
 119        case BPF_FD_TYPE_TRACEPOINT:
 120                printf("tracepoint  %s\n", buf);
 121                break;
 122        case BPF_FD_TYPE_KPROBE:
 123                if (buf[0] != '\0')
 124                        printf("kprobe  func %s  offset %llu\n", buf,
 125                               probe_offset);
 126                else
 127                        printf("kprobe  addr %llu\n", probe_addr);
 128                break;
 129        case BPF_FD_TYPE_KRETPROBE:
 130                if (buf[0] != '\0')
 131                        printf("kretprobe  func %s  offset %llu\n", buf,
 132                               probe_offset);
 133                else
 134                        printf("kretprobe  addr %llu\n", probe_addr);
 135                break;
 136        case BPF_FD_TYPE_UPROBE:
 137                printf("uprobe  filename %s  offset %llu\n", buf, probe_offset);
 138                break;
 139        case BPF_FD_TYPE_URETPROBE:
 140                printf("uretprobe  filename %s  offset %llu\n", buf,
 141                       probe_offset);
 142                break;
 143        }
 144}
 145
 146static int show_proc(const char *fpath, const struct stat *sb,
 147                     int tflag, struct FTW *ftwbuf)
 148{
 149        __u64 probe_offset, probe_addr;
 150        __u32 len, prog_id, fd_type;
 151        int err, pid = 0, fd = 0;
 152        const char *pch;
 153        char buf[4096];
 154
 155        /* prefix always /proc */
 156        pch = fpath + 5;
 157        if (*pch == '\0')
 158                return 0;
 159
 160        /* pid should be all numbers */
 161        pch++;
 162        while (isdigit(*pch)) {
 163                pid = pid * 10 + *pch - '0';
 164                pch++;
 165        }
 166        if (*pch == '\0')
 167                return 0;
 168        if (*pch != '/')
 169                return FTW_SKIP_SUBTREE;
 170
 171        /* check /proc/<pid>/fd directory */
 172        pch++;
 173        if (strncmp(pch, "fd", 2))
 174                return FTW_SKIP_SUBTREE;
 175        pch += 2;
 176        if (*pch == '\0')
 177                return 0;
 178        if (*pch != '/')
 179                return FTW_SKIP_SUBTREE;
 180
 181        /* check /proc/<pid>/fd/<fd_num> */
 182        pch++;
 183        while (isdigit(*pch)) {
 184                fd = fd * 10 + *pch - '0';
 185                pch++;
 186        }
 187        if (*pch != '\0')
 188                return FTW_SKIP_SUBTREE;
 189
 190        /* query (pid, fd) for potential perf events */
 191        len = sizeof(buf);
 192        err = bpf_task_fd_query(pid, fd, 0, buf, &len, &prog_id, &fd_type,
 193                                &probe_offset, &probe_addr);
 194        if (err < 0)
 195                return 0;
 196
 197        if (json_output)
 198                print_perf_json(pid, fd, prog_id, fd_type, buf, probe_offset,
 199                                probe_addr);
 200        else
 201                print_perf_plain(pid, fd, prog_id, fd_type, buf, probe_offset,
 202                                 probe_addr);
 203
 204        return 0;
 205}
 206
 207static int do_show(int argc, char **argv)
 208{
 209        int flags = FTW_ACTIONRETVAL | FTW_PHYS;
 210        int err = 0, nopenfd = 16;
 211
 212        if (!has_perf_query_support())
 213                return -1;
 214
 215        if (json_output)
 216                jsonw_start_array(json_wtr);
 217        if (nftw("/proc", show_proc, nopenfd, flags) == -1) {
 218                p_err("%s", strerror(errno));
 219                err = -1;
 220        }
 221        if (json_output)
 222                jsonw_end_array(json_wtr);
 223
 224        return err;
 225}
 226
 227static int do_help(int argc, char **argv)
 228{
 229        fprintf(stderr,
 230                "Usage: %s %s { show | list | help }\n"
 231                "",
 232                bin_name, argv[-2]);
 233
 234        return 0;
 235}
 236
 237static const struct cmd cmds[] = {
 238        { "show",       do_show },
 239        { "list",       do_show },
 240        { "help",       do_help },
 241        { 0 }
 242};
 243
 244int do_perf(int argc, char **argv)
 245{
 246        return cmd_select(cmds, argc, argv, do_help);
 247}
 248