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 */ 21 static int perf_query_supported; 22 static 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 57 close_fd: 58 close(fd); 59 out: 60 return perf_query_supported == 1; 61 } 62 63 static 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 113 static 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 150 static 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 211 static 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 231 static 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 244 static const struct cmd cmds[] = { 245 { "show", do_show }, 246 { "list", do_show }, 247 { "help", do_help }, 248 { 0 } 249 }; 250 251 int do_perf(int argc, char **argv) 252 { 253 return cmd_select(cmds, argc, argv, do_help); 254 } 255