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