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 /* musl libc doesn't implement these GNU extensions. They are used below
23 * to optimize out walking unnecessary subtrees of /proc, #defining them
24 * to 0 here disables the optimization but leaves the functionality otherwise
25 * unchanged.
26 */
27 #ifndef FTW_SKIP_SUBTREE
28 #define FTW_SKIP_SUBTREE 0
29 #endif
30
31 #ifndef FTW_ACTIONRETVAL
32 #define FTW_ACTIONRETVAL 0
33 #endif
34
35 /* 0: undecided, 1: supported, 2: not supported */
36 static int perf_query_supported;
has_perf_query_support(void)37 static bool has_perf_query_support(void)
38 {
39 __u64 probe_offset, probe_addr;
40 __u32 len, prog_id, fd_type;
41 char buf[256];
42 int fd;
43
44 if (perf_query_supported)
45 goto out;
46
47 fd = open("/", O_RDONLY);
48 if (fd < 0) {
49 p_err("perf_query_support: cannot open directory \"/\" (%s)",
50 strerror(errno));
51 goto out;
52 }
53
54 /* the following query will fail as no bpf attachment,
55 * the expected errno is ENOTSUPP
56 */
57 errno = 0;
58 len = sizeof(buf);
59 bpf_task_fd_query(getpid(), fd, 0, buf, &len, &prog_id,
60 &fd_type, &probe_offset, &probe_addr);
61
62 if (errno == 524 /* ENOTSUPP */) {
63 perf_query_supported = 1;
64 goto close_fd;
65 }
66
67 perf_query_supported = 2;
68 p_err("perf_query_support: %s", strerror(errno));
69 fprintf(stderr,
70 "HINT: non root or kernel doesn't support TASK_FD_QUERY\n");
71
72 close_fd:
73 close(fd);
74 out:
75 return perf_query_supported == 1;
76 }
77
print_perf_json(int pid,int fd,__u32 prog_id,__u32 fd_type,char * buf,__u64 probe_offset,__u64 probe_addr)78 static void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type,
79 char *buf, __u64 probe_offset, __u64 probe_addr)
80 {
81 jsonw_start_object(json_wtr);
82 jsonw_int_field(json_wtr, "pid", pid);
83 jsonw_int_field(json_wtr, "fd", fd);
84 jsonw_uint_field(json_wtr, "prog_id", prog_id);
85 switch (fd_type) {
86 case BPF_FD_TYPE_RAW_TRACEPOINT:
87 jsonw_string_field(json_wtr, "fd_type", "raw_tracepoint");
88 jsonw_string_field(json_wtr, "tracepoint", buf);
89 break;
90 case BPF_FD_TYPE_TRACEPOINT:
91 jsonw_string_field(json_wtr, "fd_type", "tracepoint");
92 jsonw_string_field(json_wtr, "tracepoint", buf);
93 break;
94 case BPF_FD_TYPE_KPROBE:
95 jsonw_string_field(json_wtr, "fd_type", "kprobe");
96 if (buf[0] != '\0') {
97 jsonw_string_field(json_wtr, "func", buf);
98 jsonw_lluint_field(json_wtr, "offset", probe_offset);
99 } else {
100 jsonw_lluint_field(json_wtr, "addr", probe_addr);
101 }
102 break;
103 case BPF_FD_TYPE_KRETPROBE:
104 jsonw_string_field(json_wtr, "fd_type", "kretprobe");
105 if (buf[0] != '\0') {
106 jsonw_string_field(json_wtr, "func", buf);
107 jsonw_lluint_field(json_wtr, "offset", probe_offset);
108 } else {
109 jsonw_lluint_field(json_wtr, "addr", probe_addr);
110 }
111 break;
112 case BPF_FD_TYPE_UPROBE:
113 jsonw_string_field(json_wtr, "fd_type", "uprobe");
114 jsonw_string_field(json_wtr, "filename", buf);
115 jsonw_lluint_field(json_wtr, "offset", probe_offset);
116 break;
117 case BPF_FD_TYPE_URETPROBE:
118 jsonw_string_field(json_wtr, "fd_type", "uretprobe");
119 jsonw_string_field(json_wtr, "filename", buf);
120 jsonw_lluint_field(json_wtr, "offset", probe_offset);
121 break;
122 default:
123 break;
124 }
125 jsonw_end_object(json_wtr);
126 }
127
print_perf_plain(int pid,int fd,__u32 prog_id,__u32 fd_type,char * buf,__u64 probe_offset,__u64 probe_addr)128 static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
129 char *buf, __u64 probe_offset, __u64 probe_addr)
130 {
131 printf("pid %d fd %d: prog_id %u ", pid, fd, prog_id);
132 switch (fd_type) {
133 case BPF_FD_TYPE_RAW_TRACEPOINT:
134 printf("raw_tracepoint %s\n", buf);
135 break;
136 case BPF_FD_TYPE_TRACEPOINT:
137 printf("tracepoint %s\n", buf);
138 break;
139 case BPF_FD_TYPE_KPROBE:
140 if (buf[0] != '\0')
141 printf("kprobe func %s offset %llu\n", buf,
142 probe_offset);
143 else
144 printf("kprobe addr %llu\n", probe_addr);
145 break;
146 case BPF_FD_TYPE_KRETPROBE:
147 if (buf[0] != '\0')
148 printf("kretprobe func %s offset %llu\n", buf,
149 probe_offset);
150 else
151 printf("kretprobe addr %llu\n", probe_addr);
152 break;
153 case BPF_FD_TYPE_UPROBE:
154 printf("uprobe filename %s offset %llu\n", buf, probe_offset);
155 break;
156 case BPF_FD_TYPE_URETPROBE:
157 printf("uretprobe filename %s offset %llu\n", buf,
158 probe_offset);
159 break;
160 default:
161 break;
162 }
163 }
164
show_proc(void)165 static int show_proc(void)
166 {
167 struct dirent *proc_de, *pid_fd_de;
168 __u64 probe_offset, probe_addr;
169 __u32 len, prog_id, fd_type;
170 DIR *proc, *pid_fd;
171 int err, pid, fd;
172 const char *pch;
173 char buf[4096];
174
175 proc = opendir("/proc");
176 if (!proc)
177 return -1;
178
179 while ((proc_de = readdir(proc))) {
180 pid = 0;
181 pch = proc_de->d_name;
182
183 /* pid should be all numbers */
184 while (isdigit(*pch)) {
185 pid = pid * 10 + *pch - '0';
186 pch++;
187 }
188 if (*pch != '\0')
189 continue;
190
191 err = snprintf(buf, sizeof(buf), "/proc/%s/fd", proc_de->d_name);
192 if (err < 0 || err >= (int)sizeof(buf))
193 continue;
194
195 pid_fd = opendir(buf);
196 if (!pid_fd)
197 continue;
198
199 while ((pid_fd_de = readdir(pid_fd))) {
200 fd = 0;
201 pch = pid_fd_de->d_name;
202
203 /* fd should be all numbers */
204 while (isdigit(*pch)) {
205 fd = fd * 10 + *pch - '0';
206 pch++;
207 }
208 if (*pch != '\0')
209 continue;
210
211 /* query (pid, fd) for potential perf events */
212 len = sizeof(buf);
213 err = bpf_task_fd_query(pid, fd, 0, buf, &len,
214 &prog_id, &fd_type,
215 &probe_offset, &probe_addr);
216 if (err < 0)
217 continue;
218
219 if (json_output)
220 print_perf_json(pid, fd, prog_id, fd_type, buf,
221 probe_offset, probe_addr);
222 else
223 print_perf_plain(pid, fd, prog_id, fd_type, buf,
224 probe_offset, probe_addr);
225 }
226 closedir(pid_fd);
227 }
228 closedir(proc);
229 return 0;
230 }
231
do_show(int argc,char ** argv)232 static int do_show(int argc, char **argv)
233 {
234 int err;
235
236 if (!has_perf_query_support())
237 return -1;
238
239 if (json_output)
240 jsonw_start_array(json_wtr);
241 err = show_proc();
242 if (json_output)
243 jsonw_end_array(json_wtr);
244
245 return err;
246 }
247
do_help(int argc,char ** argv)248 static int do_help(int argc, char **argv)
249 {
250 fprintf(stderr,
251 "Usage: %1$s %2$s { show | list }\n"
252 " %1$s %2$s help\n"
253 "\n"
254 " " HELP_SPEC_OPTIONS " }\n"
255 "",
256 bin_name, argv[-2]);
257
258 return 0;
259 }
260
261 static const struct cmd cmds[] = {
262 { "show", do_show },
263 { "list", do_show },
264 { "help", do_help },
265 { 0 }
266 };
267
do_perf(int argc,char ** argv)268 int do_perf(int argc, char **argv)
269 {
270 return cmd_select(cmds, argc, argv, do_help);
271 }
272