Merge tag 'mvebu-dt-4.19-1' of git://git.infradead.org/linux-mvebu into next/dt
[linux-block.git] / tools / bpf / bpftool / perf.c
CommitLineData
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 */
21static int perf_query_supported;
22static 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
57close_fd:
58 close(fd);
59out:
60 return perf_query_supported == 1;
61}
62
63static 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
111static 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
146static 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
207static 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
227static 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
237static const struct cmd cmds[] = {
238 { "show", do_show },
239 { "list", do_show },
240 { "help", do_help },
241 { 0 }
242};
243
244int do_perf(int argc, char **argv)
245{
246 return cmd_select(cmds, argc, argv, do_help);
247}