xref: /openbmc/linux/tools/bpf/bpftool/perf.c (revision ac73d4bf)
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 }
259