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