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