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