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