Commit | Line | Data |
---|---|---|
b04df400 YS |
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 | ||
73df93c5 | 32 | fd = open("/", O_RDONLY); |
b04df400 | 33 | if (fd < 0) { |
73df93c5 YS |
34 | p_err("perf_query_support: cannot open directory \"/\" (%s)", |
35 | strerror(errno)); | |
b04df400 YS |
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 | ||
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 | } | |
108 | jsonw_end_object(json_wtr); | |
109 | } | |
110 | ||
111 | static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type, | |
112 | char *buf, __u64 probe_offset, __u64 probe_addr) | |
113 | { | |
114 | printf("pid %d fd %d: prog_id %u ", pid, fd, prog_id); | |
115 | switch (fd_type) { | |
116 | case BPF_FD_TYPE_RAW_TRACEPOINT: | |
117 | printf("raw_tracepoint %s\n", buf); | |
118 | break; | |
119 | case BPF_FD_TYPE_TRACEPOINT: | |
120 | printf("tracepoint %s\n", buf); | |
121 | break; | |
122 | case BPF_FD_TYPE_KPROBE: | |
123 | if (buf[0] != '\0') | |
124 | printf("kprobe func %s offset %llu\n", buf, | |
125 | probe_offset); | |
126 | else | |
127 | printf("kprobe addr %llu\n", probe_addr); | |
128 | break; | |
129 | case BPF_FD_TYPE_KRETPROBE: | |
130 | if (buf[0] != '\0') | |
131 | printf("kretprobe func %s offset %llu\n", buf, | |
132 | probe_offset); | |
133 | else | |
134 | printf("kretprobe addr %llu\n", probe_addr); | |
135 | break; | |
136 | case BPF_FD_TYPE_UPROBE: | |
137 | printf("uprobe filename %s offset %llu\n", buf, probe_offset); | |
138 | break; | |
139 | case BPF_FD_TYPE_URETPROBE: | |
140 | printf("uretprobe filename %s offset %llu\n", buf, | |
141 | probe_offset); | |
142 | break; | |
143 | } | |
144 | } | |
145 | ||
146 | static int show_proc(const char *fpath, const struct stat *sb, | |
147 | int tflag, struct FTW *ftwbuf) | |
148 | { | |
149 | __u64 probe_offset, probe_addr; | |
150 | __u32 len, prog_id, fd_type; | |
151 | int err, pid = 0, fd = 0; | |
152 | const char *pch; | |
153 | char buf[4096]; | |
154 | ||
155 | /* prefix always /proc */ | |
156 | pch = fpath + 5; | |
157 | if (*pch == '\0') | |
158 | return 0; | |
159 | ||
160 | /* pid should be all numbers */ | |
161 | pch++; | |
162 | while (isdigit(*pch)) { | |
163 | pid = pid * 10 + *pch - '0'; | |
164 | pch++; | |
165 | } | |
166 | if (*pch == '\0') | |
167 | return 0; | |
168 | if (*pch != '/') | |
169 | return FTW_SKIP_SUBTREE; | |
170 | ||
171 | /* check /proc/<pid>/fd directory */ | |
172 | pch++; | |
173 | if (strncmp(pch, "fd", 2)) | |
174 | return FTW_SKIP_SUBTREE; | |
175 | pch += 2; | |
176 | if (*pch == '\0') | |
177 | return 0; | |
178 | if (*pch != '/') | |
179 | return FTW_SKIP_SUBTREE; | |
180 | ||
181 | /* check /proc/<pid>/fd/<fd_num> */ | |
182 | pch++; | |
183 | while (isdigit(*pch)) { | |
184 | fd = fd * 10 + *pch - '0'; | |
185 | pch++; | |
186 | } | |
187 | if (*pch != '\0') | |
188 | return FTW_SKIP_SUBTREE; | |
189 | ||
190 | /* query (pid, fd) for potential perf events */ | |
191 | len = sizeof(buf); | |
192 | err = bpf_task_fd_query(pid, fd, 0, buf, &len, &prog_id, &fd_type, | |
193 | &probe_offset, &probe_addr); | |
194 | if (err < 0) | |
195 | return 0; | |
196 | ||
197 | if (json_output) | |
198 | print_perf_json(pid, fd, prog_id, fd_type, buf, probe_offset, | |
199 | probe_addr); | |
200 | else | |
201 | print_perf_plain(pid, fd, prog_id, fd_type, buf, probe_offset, | |
202 | probe_addr); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static int do_show(int argc, char **argv) | |
208 | { | |
209 | int flags = FTW_ACTIONRETVAL | FTW_PHYS; | |
210 | int err = 0, nopenfd = 16; | |
211 | ||
212 | if (!has_perf_query_support()) | |
213 | return -1; | |
214 | ||
215 | if (json_output) | |
216 | jsonw_start_array(json_wtr); | |
217 | if (nftw("/proc", show_proc, nopenfd, flags) == -1) { | |
218 | p_err("%s", strerror(errno)); | |
219 | err = -1; | |
220 | } | |
221 | if (json_output) | |
222 | jsonw_end_array(json_wtr); | |
223 | ||
224 | return err; | |
225 | } | |
226 | ||
227 | static int do_help(int argc, char **argv) | |
228 | { | |
229 | fprintf(stderr, | |
230 | "Usage: %s %s { show | list | help }\n" | |
231 | "", | |
232 | bin_name, argv[-2]); | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | static const struct cmd cmds[] = { | |
238 | { "show", do_show }, | |
239 | { "list", do_show }, | |
240 | { "help", do_help }, | |
241 | { 0 } | |
242 | }; | |
243 | ||
244 | int do_perf(int argc, char **argv) | |
245 | { | |
246 | return cmd_select(cmds, argc, argv, do_help); | |
247 | } |