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 #ifndef _GNU_SOURCE 6 #define _GNU_SOURCE 7 #endif 8 #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 */ 23 static int perf_query_supported; 24 static 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 ENOTSUPP 43 */ 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 59 close_fd: 60 close(fd); 61 out: 62 return perf_query_supported == 1; 63 } 64 65 static 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 115 static 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 else 131 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 else 138 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 152 static 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 else 210 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 219 static 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 235 static 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 248 static const struct cmd cmds[] = { 249 { "show", do_show }, 250 { "list", do_show }, 251 { "help", do_help }, 252 { 0 } 253 }; 254 255 int do_perf(int argc, char **argv) 256 { 257 return cmd_select(cmds, argc, argv, do_help); 258 } 259