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 <dirent.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;
has_perf_query_support(void)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
print_perf_json(int pid,int fd,__u32 prog_id,__u32 fd_type,char * buf,__u64 probe_offset,__u64 probe_addr)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
print_perf_plain(int pid,int fd,__u32 prog_id,__u32 fd_type,char * buf,__u64 probe_offset,__u64 probe_addr)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
show_proc(void)150 static int show_proc(void)
151 {
152 struct dirent *proc_de, *pid_fd_de;
153 __u64 probe_offset, probe_addr;
154 __u32 len, prog_id, fd_type;
155 DIR *proc, *pid_fd;
156 int err, pid, fd;
157 const char *pch;
158 char buf[4096];
159
160 proc = opendir("/proc");
161 if (!proc)
162 return -1;
163
164 while ((proc_de = readdir(proc))) {
165 pid = 0;
166 pch = proc_de->d_name;
167
168 /* pid should be all numbers */
169 while (isdigit(*pch)) {
170 pid = pid * 10 + *pch - '0';
171 pch++;
172 }
173 if (*pch != '\0')
174 continue;
175
176 err = snprintf(buf, sizeof(buf), "/proc/%s/fd", proc_de->d_name);
177 if (err < 0 || err >= (int)sizeof(buf))
178 continue;
179
180 pid_fd = opendir(buf);
181 if (!pid_fd)
182 continue;
183
184 while ((pid_fd_de = readdir(pid_fd))) {
185 fd = 0;
186 pch = pid_fd_de->d_name;
187
188 /* fd should be all numbers */
189 while (isdigit(*pch)) {
190 fd = fd * 10 + *pch - '0';
191 pch++;
192 }
193 if (*pch != '\0')
194 continue;
195
196 /* query (pid, fd) for potential perf events */
197 len = sizeof(buf);
198 err = bpf_task_fd_query(pid, fd, 0, buf, &len,
199 &prog_id, &fd_type,
200 &probe_offset, &probe_addr);
201 if (err < 0)
202 continue;
203
204 if (json_output)
205 print_perf_json(pid, fd, prog_id, fd_type, buf,
206 probe_offset, probe_addr);
207 else
208 print_perf_plain(pid, fd, prog_id, fd_type, buf,
209 probe_offset, probe_addr);
210 }
211 closedir(pid_fd);
212 }
213 closedir(proc);
214 return 0;
215 }
216
do_show(int argc,char ** argv)217 static int do_show(int argc, char **argv)
218 {
219 int err;
220
221 if (!has_perf_query_support())
222 return -1;
223
224 if (json_output)
225 jsonw_start_array(json_wtr);
226 err = show_proc();
227 if (json_output)
228 jsonw_end_array(json_wtr);
229
230 return err;
231 }
232
do_help(int argc,char ** argv)233 static int do_help(int argc, char **argv)
234 {
235 fprintf(stderr,
236 "Usage: %1$s %2$s { show | list }\n"
237 " %1$s %2$s help }\n"
238 "\n"
239 " " HELP_SPEC_OPTIONS " }\n"
240 "",
241 bin_name, argv[-2]);
242
243 return 0;
244 }
245
246 static const struct cmd cmds[] = {
247 { "show", do_show },
248 { "list", do_show },
249 { "help", do_help },
250 { 0 }
251 };
252
do_perf(int argc,char ** argv)253 int do_perf(int argc, char **argv)
254 {
255 return cmd_select(cmds, argc, argv, do_help);
256 }
257