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(fmt: "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(fmt: "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(self: json_wtr); |
69 | jsonw_int_field(self: json_wtr, prop: "pid" , num: pid); |
70 | jsonw_int_field(self: json_wtr, prop: "fd" , num: fd); |
71 | jsonw_uint_field(self: json_wtr, prop: "prog_id" , num: prog_id); |
72 | switch (fd_type) { |
73 | case BPF_FD_TYPE_RAW_TRACEPOINT: |
74 | jsonw_string_field(self: json_wtr, prop: "fd_type" , val: "raw_tracepoint" ); |
75 | jsonw_string_field(self: json_wtr, prop: "tracepoint" , val: buf); |
76 | break; |
77 | case BPF_FD_TYPE_TRACEPOINT: |
78 | jsonw_string_field(self: json_wtr, prop: "fd_type" , val: "tracepoint" ); |
79 | jsonw_string_field(self: json_wtr, prop: "tracepoint" , val: buf); |
80 | break; |
81 | case BPF_FD_TYPE_KPROBE: |
82 | jsonw_string_field(self: json_wtr, prop: "fd_type" , val: "kprobe" ); |
83 | if (buf[0] != '\0') { |
84 | jsonw_string_field(self: json_wtr, prop: "func" , val: buf); |
85 | jsonw_lluint_field(self: json_wtr, prop: "offset" , num: probe_offset); |
86 | } else { |
87 | jsonw_lluint_field(self: json_wtr, prop: "addr" , num: probe_addr); |
88 | } |
89 | break; |
90 | case BPF_FD_TYPE_KRETPROBE: |
91 | jsonw_string_field(self: json_wtr, prop: "fd_type" , val: "kretprobe" ); |
92 | if (buf[0] != '\0') { |
93 | jsonw_string_field(self: json_wtr, prop: "func" , val: buf); |
94 | jsonw_lluint_field(self: json_wtr, prop: "offset" , num: probe_offset); |
95 | } else { |
96 | jsonw_lluint_field(self: json_wtr, prop: "addr" , num: probe_addr); |
97 | } |
98 | break; |
99 | case BPF_FD_TYPE_UPROBE: |
100 | jsonw_string_field(self: json_wtr, prop: "fd_type" , val: "uprobe" ); |
101 | jsonw_string_field(self: json_wtr, prop: "filename" , val: buf); |
102 | jsonw_lluint_field(self: json_wtr, prop: "offset" , num: probe_offset); |
103 | break; |
104 | case BPF_FD_TYPE_URETPROBE: |
105 | jsonw_string_field(self: json_wtr, prop: "fd_type" , val: "uretprobe" ); |
106 | jsonw_string_field(self: json_wtr, prop: "filename" , val: buf); |
107 | jsonw_lluint_field(self: json_wtr, prop: "offset" , num: probe_offset); |
108 | break; |
109 | default: |
110 | break; |
111 | } |
112 | jsonw_end_object(self: 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(c: *pch)) { |
172 | pid = pid * 10 + *pch - '0'; |
173 | pch++; |
174 | } |
175 | if (*pch != '\0') |
176 | continue; |
177 | |
178 | err = snprintf(buf, size: sizeof(buf), fmt: "/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(c: *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(self: json_wtr); |
228 | err = show_proc(); |
229 | if (json_output) |
230 | jsonw_end_array(self: 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, help: do_help); |
258 | } |
259 | |