xref: /openbmc/linux/tools/bpf/bpftool/perf.c (revision faa16bc4)
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 */
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(bin_name, O_RDONLY);
33 	if (fd < 0) {
34 		p_err("perf_query_support: %s", strerror(errno));
35 		goto out;
36 	}
37 
38 	/* the following query will fail as no bpf attachment,
39 	 * the expected errno is ENOTSUPP
40 	 */
41 	errno = 0;
42 	len = sizeof(buf);
43 	bpf_task_fd_query(getpid(), fd, 0, buf, &len, &prog_id,
44 			  &fd_type, &probe_offset, &probe_addr);
45 
46 	if (errno == 524 /* ENOTSUPP */) {
47 		perf_query_supported = 1;
48 		goto close_fd;
49 	}
50 
51 	perf_query_supported = 2;
52 	p_err("perf_query_support: %s", strerror(errno));
53 	fprintf(stderr,
54 		"HINT: non root or kernel doesn't support TASK_FD_QUERY\n");
55 
56 close_fd:
57 	close(fd);
58 out:
59 	return perf_query_supported == 1;
60 }
61 
62 static void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type,
63 			    char *buf, __u64 probe_offset, __u64 probe_addr)
64 {
65 	jsonw_start_object(json_wtr);
66 	jsonw_int_field(json_wtr, "pid", pid);
67 	jsonw_int_field(json_wtr, "fd", fd);
68 	jsonw_uint_field(json_wtr, "prog_id", prog_id);
69 	switch (fd_type) {
70 	case BPF_FD_TYPE_RAW_TRACEPOINT:
71 		jsonw_string_field(json_wtr, "fd_type", "raw_tracepoint");
72 		jsonw_string_field(json_wtr, "tracepoint", buf);
73 		break;
74 	case BPF_FD_TYPE_TRACEPOINT:
75 		jsonw_string_field(json_wtr, "fd_type", "tracepoint");
76 		jsonw_string_field(json_wtr, "tracepoint", buf);
77 		break;
78 	case BPF_FD_TYPE_KPROBE:
79 		jsonw_string_field(json_wtr, "fd_type", "kprobe");
80 		if (buf[0] != '\0') {
81 			jsonw_string_field(json_wtr, "func", buf);
82 			jsonw_lluint_field(json_wtr, "offset", probe_offset);
83 		} else {
84 			jsonw_lluint_field(json_wtr, "addr", probe_addr);
85 		}
86 		break;
87 	case BPF_FD_TYPE_KRETPROBE:
88 		jsonw_string_field(json_wtr, "fd_type", "kretprobe");
89 		if (buf[0] != '\0') {
90 			jsonw_string_field(json_wtr, "func", buf);
91 			jsonw_lluint_field(json_wtr, "offset", probe_offset);
92 		} else {
93 			jsonw_lluint_field(json_wtr, "addr", probe_addr);
94 		}
95 		break;
96 	case BPF_FD_TYPE_UPROBE:
97 		jsonw_string_field(json_wtr, "fd_type", "uprobe");
98 		jsonw_string_field(json_wtr, "filename", buf);
99 		jsonw_lluint_field(json_wtr, "offset", probe_offset);
100 		break;
101 	case BPF_FD_TYPE_URETPROBE:
102 		jsonw_string_field(json_wtr, "fd_type", "uretprobe");
103 		jsonw_string_field(json_wtr, "filename", buf);
104 		jsonw_lluint_field(json_wtr, "offset", probe_offset);
105 		break;
106 	}
107 	jsonw_end_object(json_wtr);
108 }
109 
110 static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
111 			     char *buf, __u64 probe_offset, __u64 probe_addr)
112 {
113 	printf("pid %d  fd %d: prog_id %u  ", pid, fd, prog_id);
114 	switch (fd_type) {
115 	case BPF_FD_TYPE_RAW_TRACEPOINT:
116 		printf("raw_tracepoint  %s\n", buf);
117 		break;
118 	case BPF_FD_TYPE_TRACEPOINT:
119 		printf("tracepoint  %s\n", buf);
120 		break;
121 	case BPF_FD_TYPE_KPROBE:
122 		if (buf[0] != '\0')
123 			printf("kprobe  func %s  offset %llu\n", buf,
124 			       probe_offset);
125 		else
126 			printf("kprobe  addr %llu\n", probe_addr);
127 		break;
128 	case BPF_FD_TYPE_KRETPROBE:
129 		if (buf[0] != '\0')
130 			printf("kretprobe  func %s  offset %llu\n", buf,
131 			       probe_offset);
132 		else
133 			printf("kretprobe  addr %llu\n", probe_addr);
134 		break;
135 	case BPF_FD_TYPE_UPROBE:
136 		printf("uprobe  filename %s  offset %llu\n", buf, probe_offset);
137 		break;
138 	case BPF_FD_TYPE_URETPROBE:
139 		printf("uretprobe  filename %s  offset %llu\n", buf,
140 		       probe_offset);
141 		break;
142 	}
143 }
144 
145 static int show_proc(const char *fpath, const struct stat *sb,
146 		     int tflag, struct FTW *ftwbuf)
147 {
148 	__u64 probe_offset, probe_addr;
149 	__u32 len, prog_id, fd_type;
150 	int err, pid = 0, fd = 0;
151 	const char *pch;
152 	char buf[4096];
153 
154 	/* prefix always /proc */
155 	pch = fpath + 5;
156 	if (*pch == '\0')
157 		return 0;
158 
159 	/* pid should be all numbers */
160 	pch++;
161 	while (isdigit(*pch)) {
162 		pid = pid * 10 + *pch - '0';
163 		pch++;
164 	}
165 	if (*pch == '\0')
166 		return 0;
167 	if (*pch != '/')
168 		return FTW_SKIP_SUBTREE;
169 
170 	/* check /proc/<pid>/fd directory */
171 	pch++;
172 	if (strncmp(pch, "fd", 2))
173 		return FTW_SKIP_SUBTREE;
174 	pch += 2;
175 	if (*pch == '\0')
176 		return 0;
177 	if (*pch != '/')
178 		return FTW_SKIP_SUBTREE;
179 
180 	/* check /proc/<pid>/fd/<fd_num> */
181 	pch++;
182 	while (isdigit(*pch)) {
183 		fd = fd * 10 + *pch - '0';
184 		pch++;
185 	}
186 	if (*pch != '\0')
187 		return FTW_SKIP_SUBTREE;
188 
189 	/* query (pid, fd) for potential perf events */
190 	len = sizeof(buf);
191 	err = bpf_task_fd_query(pid, fd, 0, buf, &len, &prog_id, &fd_type,
192 				&probe_offset, &probe_addr);
193 	if (err < 0)
194 		return 0;
195 
196 	if (json_output)
197 		print_perf_json(pid, fd, prog_id, fd_type, buf, probe_offset,
198 				probe_addr);
199 	else
200 		print_perf_plain(pid, fd, prog_id, fd_type, buf, probe_offset,
201 				 probe_addr);
202 
203 	return 0;
204 }
205 
206 static int do_show(int argc, char **argv)
207 {
208 	int flags = FTW_ACTIONRETVAL | FTW_PHYS;
209 	int err = 0, nopenfd = 16;
210 
211 	if (!has_perf_query_support())
212 		return -1;
213 
214 	if (json_output)
215 		jsonw_start_array(json_wtr);
216 	if (nftw("/proc", show_proc, nopenfd, flags) == -1) {
217 		p_err("%s", strerror(errno));
218 		err = -1;
219 	}
220 	if (json_output)
221 		jsonw_end_array(json_wtr);
222 
223 	return err;
224 }
225 
226 static int do_help(int argc, char **argv)
227 {
228 	fprintf(stderr,
229 		"Usage: %s %s { show | list | help }\n"
230 		"",
231 		bin_name, argv[-2]);
232 
233 	return 0;
234 }
235 
236 static const struct cmd cmds[] = {
237 	{ "show",	do_show },
238 	{ "list",	do_show },
239 	{ "help",	do_help },
240 	{ 0 }
241 };
242 
243 int do_perf(int argc, char **argv)
244 {
245 	return cmd_select(cmds, argc, argv, do_help);
246 }
247