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