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