xref: /aosp_15_r20/external/bcc/libbpf-tools/vfsstat.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2020 Anton Protopopov
3 //
4 // Based on vfsstat(8) from BCC by Brendan Gregg
5 #include <argp.h>
6 #include <unistd.h>
7 #include <time.h>
8 #include <bpf/bpf.h>
9 #include "vfsstat.h"
10 #include "vfsstat.skel.h"
11 #include "btf_helpers.h"
12 #include "trace_helpers.h"
13 
14 const char *argp_program_version = "vfsstat 0.1";
15 const char *argp_program_bug_address =
16 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
17 static const char argp_program_doc[] =
18 	"\nvfsstat: Count some VFS calls\n"
19 	"\n"
20 	"EXAMPLES:\n"
21 	"    vfsstat      # interval one second\n"
22 	"    vfsstat 5 3  # interval five seconds, three output lines\n";
23 static char args_doc[] = "[interval [count]]";
24 
25 static const struct argp_option opts[] = {
26 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
27 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
28 	{},
29 };
30 
31 static struct env {
32 	bool verbose;
33 	int count;
34 	int interval;
35 } env = {
36 	.interval = 1,	/* once a second */
37 };
38 
parse_arg(int key,char * arg,struct argp_state * state)39 static error_t parse_arg(int key, char *arg, struct argp_state *state)
40 {
41 	long interval;
42 	long count;
43 
44 	switch (key) {
45 	case 'h':
46 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
47 		break;
48 	case 'v':
49 		env.verbose = true;
50 		break;
51 	case ARGP_KEY_ARG:
52 		switch (state->arg_num) {
53 		case 0:
54 			errno = 0;
55 			interval = strtol(arg, NULL, 10);
56 			if (errno || interval <= 0 || interval > INT_MAX) {
57 				fprintf(stderr, "invalid interval: %s\n", arg);
58 				argp_usage(state);
59 			}
60 			env.interval = interval;
61 			break;
62 		case 1:
63 			errno = 0;
64 			count = strtol(arg, NULL, 10);
65 			if (errno || count < 0 || count > INT_MAX) {
66 				fprintf(stderr, "invalid count: %s\n", arg);
67 				argp_usage(state);
68 			}
69 			env.count = count;
70 			break;
71 		default:
72 			argp_usage(state);
73 			break;
74 		}
75 		break;
76 	default:
77 		return ARGP_ERR_UNKNOWN;
78 	}
79 	return 0;
80 }
81 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)82 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
83 {
84 	if (level == LIBBPF_DEBUG && !env.verbose)
85 		return 0;
86 	return vfprintf(stderr, format, args);
87 }
88 
strftime_now(char * s,size_t max,const char * format)89 static const char *strftime_now(char *s, size_t max, const char *format)
90 {
91 	struct tm *tm;
92 	time_t t;
93 
94 	t = time(NULL);
95 	tm = localtime(&t);
96 	if (tm == NULL) {
97 		fprintf(stderr, "localtime: %s\n", strerror(errno));
98 		return "<failed>";
99 	}
100 	if (strftime(s, max, format, tm) == 0) {
101 		fprintf(stderr, "strftime error\n");
102 		return "<failed>";
103 	}
104 	return s;
105 }
106 
107 static const char *stat_types_names[] = {
108 	[S_READ] = "READ",
109 	[S_WRITE] = "WRITE",
110 	[S_FSYNC] = "FSYNC",
111 	[S_OPEN] = "OPEN",
112 	[S_CREATE] = "CREATE",
113 };
114 
print_header(void)115 static void print_header(void)
116 {
117 	int i;
118 
119 	printf("%-8s  ", "TIME");
120 	for (i = 0; i < S_MAXSTAT; i++)
121 		printf(" %6s/s", stat_types_names[i]);
122 	printf("\n");
123 }
124 
print_and_reset_stats(__u64 stats[S_MAXSTAT])125 static void print_and_reset_stats(__u64 stats[S_MAXSTAT])
126 {
127 	char s[16];
128 	__u64 val;
129 	int i;
130 
131 	printf("%-8s: ", strftime_now(s, sizeof(s), "%H:%M:%S"));
132 	for (i = 0; i < S_MAXSTAT; i++) {
133 		val = __atomic_exchange_n(&stats[i], 0, __ATOMIC_RELAXED);
134 		printf(" %8llu", val / env.interval);
135 	}
136 	printf("\n");
137 }
138 
main(int argc,char ** argv)139 int main(int argc, char **argv)
140 {
141 	LIBBPF_OPTS(bpf_object_open_opts, open_opts);
142 	static const struct argp argp = {
143 		.options = opts,
144 		.parser = parse_arg,
145 		.doc = argp_program_doc,
146 		.args_doc = args_doc,
147 	};
148 	struct vfsstat_bpf *skel;
149 	int err;
150 
151 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
152 	if (err)
153 		return err;
154 
155 	libbpf_set_print(libbpf_print_fn);
156 
157 
158 	err = ensure_core_btf(&open_opts);
159 	if (err) {
160 		fprintf(stderr, "failed to fetch necessary BTF for CO-RE: %s\n", strerror(-err));
161 		return 1;
162 	}
163 
164 	skel = vfsstat_bpf__open();
165 	if (!skel) {
166 		fprintf(stderr, "failed to open BPF skelect\n");
167 		return 1;
168 	}
169 
170 	/* It fallbacks to kprobes when kernel does not support fentry. */
171 	if (fentry_can_attach("vfs_read", NULL)) {
172 		bpf_program__set_autoload(skel->progs.kprobe_vfs_read, false);
173 		bpf_program__set_autoload(skel->progs.kprobe_vfs_write, false);
174 		bpf_program__set_autoload(skel->progs.kprobe_vfs_fsync, false);
175 		bpf_program__set_autoload(skel->progs.kprobe_vfs_open, false);
176 		bpf_program__set_autoload(skel->progs.kprobe_vfs_create, false);
177 	} else {
178 		bpf_program__set_autoload(skel->progs.fentry_vfs_read, false);
179 		bpf_program__set_autoload(skel->progs.fentry_vfs_write, false);
180 		bpf_program__set_autoload(skel->progs.fentry_vfs_fsync, false);
181 		bpf_program__set_autoload(skel->progs.fentry_vfs_open, false);
182 		bpf_program__set_autoload(skel->progs.fentry_vfs_create, false);
183 	}
184 
185 	err = vfsstat_bpf__load(skel);
186 	if (err) {
187 		fprintf(stderr, "failed to load BPF skelect: %d\n", err);
188 		goto cleanup;
189 	}
190 
191 	if (!skel->bss) {
192 		fprintf(stderr, "Memory-mapping BPF maps is supported starting from Linux 5.7, please upgrade.\n");
193 		goto cleanup;
194 	}
195 
196 	err = vfsstat_bpf__attach(skel);
197 	if (err) {
198 		fprintf(stderr, "failed to attach BPF programs: %s\n",
199 				strerror(-err));
200 		goto cleanup;
201 	}
202 
203 	print_header();
204 	do {
205 		sleep(env.interval);
206 		print_and_reset_stats(skel->bss->stats);
207 	} while (!env.count || --env.count);
208 
209 cleanup:
210 	vfsstat_bpf__destroy(skel);
211 	cleanup_core_btf(&open_opts);
212 
213 	return err != 0;
214 }
215