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 */
23static int perf_query_supported;
24static 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
59close_fd:
60 close(fd);
61out:
62 return perf_query_supported == 1;
63}
64
65static 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
115static 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
152static 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
219static 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
235static 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
248static const struct cmd cmds[] = {
249 { "show", do_show },
250 { "list", do_show },
251 { "help", do_help },
252 { 0 }
253};
254
255int do_perf(int argc, char **argv)
256{
257 return cmd_select(cmds, argc, argv, help: do_help);
258}
259

source code of linux/tools/bpf/bpftool/perf.c