xref: /aosp_15_r20/external/bcc/libbpf-tools/readahead.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2020 Wenbo Zhang
3 //
4 // Based on readahead(8) from BPF-Perf-Tools-Book by Brendan Gregg.
5 // 8-Jun-2020   Wenbo Zhang   Created this.
6 #include <argp.h>
7 #include <signal.h>
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <bpf/libbpf.h>
11 #include <bpf/bpf.h>
12 #include "readahead.h"
13 #include "readahead.skel.h"
14 #include "trace_helpers.h"
15 
16 static struct env {
17 	int duration;
18 	bool verbose;
19 } env = {
20 	.duration = -1
21 };
22 
23 static volatile bool exiting;
24 
25 const char *argp_program_version = "readahead 0.1";
26 const char *argp_program_bug_address =
27 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
28 const char argp_program_doc[] =
29 "Show fs automatic read-ahead usage.\n"
30 "\n"
31 "USAGE: readahead [--help] [-d DURATION]\n"
32 "\n"
33 "EXAMPLES:\n"
34 "    readahead              # summarize on-CPU time as a histogram\n"
35 "    readahead -d 10        # trace for 10 seconds only\n";
36 
37 static const struct argp_option opts[] = {
38 	{ "duration", 'd', "DURATION", 0, "Duration to trace"},
39 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
40 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
41 	{},
42 };
43 
parse_arg(int key,char * arg,struct argp_state * state)44 static error_t parse_arg(int key, char *arg, struct argp_state *state)
45 {
46 	switch (key) {
47 	case 'h':
48 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
49 		break;
50 	case 'v':
51 		env.verbose = true;
52 		break;
53 	case 'd':
54 		errno = 0;
55 		env.duration = strtol(arg, NULL, 10);
56 		if (errno || env.duration <= 0) {
57 			fprintf(stderr, "Invalid duration: %s\n", arg);
58 			argp_usage(state);
59 		}
60 		break;
61 	default:
62 		return ARGP_ERR_UNKNOWN;
63 	}
64 	return 0;
65 }
66 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)67 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
68 {
69 	if (level == LIBBPF_DEBUG && !env.verbose)
70 		return 0;
71 	return vfprintf(stderr, format, args);
72 }
73 
sig_handler(int sig)74 static void sig_handler(int sig)
75 {
76 	exiting = true;
77 }
78 
readahead__set_attach_target(struct bpf_program * prog)79 static int readahead__set_attach_target(struct bpf_program *prog)
80 {
81 	int err;
82 
83 	err = bpf_program__set_attach_target(prog, 0, "do_page_cache_ra");
84 	if (!err)
85 		return 0;
86 
87 	err = bpf_program__set_attach_target(prog, 0,
88 					"__do_page_cache_readahead");
89 	if (!err)
90 		return 0;
91 
92 	fprintf(stderr, "failed to set attach target for %s: %s\n",
93 		bpf_program__name(prog), strerror(-err));
94 	return err;
95 }
96 
main(int argc,char ** argv)97 int main(int argc, char **argv)
98 {
99 	static const struct argp argp = {
100 		.options = opts,
101 		.parser = parse_arg,
102 		.doc = argp_program_doc,
103 	};
104 	struct readahead_bpf *obj;
105 	struct hist *histp;
106 	int err;
107 
108 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
109 	if (err)
110 		return err;
111 
112 	libbpf_set_print(libbpf_print_fn);
113 
114 	obj = readahead_bpf__open();
115 	if (!obj) {
116 		fprintf(stderr, "failed to open BPF object\n");
117 		return 1;
118 	}
119 
120 	/*
121 	 * Starting from v5.10-rc1 (8238287), __do_page_cache_readahead has
122 	 * renamed to do_page_cache_ra. So we specify the function dynamically.
123 	 */
124 	err = readahead__set_attach_target(obj->progs.do_page_cache_ra);
125 	if (err)
126 		goto cleanup;
127 	err = readahead__set_attach_target(obj->progs.do_page_cache_ra_ret);
128 	if (err)
129 		goto cleanup;
130 
131 	err = readahead_bpf__load(obj);
132 	if (err) {
133 		fprintf(stderr, "failed to load BPF object\n");
134 		goto cleanup;
135 	}
136 
137 	if (!obj->bss) {
138 		fprintf(stderr, "Memory-mapping BPF maps is supported starting from Linux 5.7, please upgrade.\n");
139 		goto cleanup;
140 	}
141 
142 	err = readahead_bpf__attach(obj);
143 	if (err) {
144 		fprintf(stderr, "failed to attach BPF programs\n");
145 		goto cleanup;
146 	}
147 
148 	signal(SIGINT, sig_handler);
149 
150 	printf("Tracing fs read-ahead ... Hit Ctrl-C to end.\n");
151 
152 	sleep(env.duration);
153 	printf("\n");
154 
155 	histp = &obj->bss->hist;
156 
157 	printf("Readahead unused/total pages: %d/%d\n",
158 		histp->unused, histp->total);
159 	print_log2_hist(histp->slots, MAX_SLOTS, "msecs");
160 
161 cleanup:
162 	readahead_bpf__destroy(obj);
163 	return err != 0;
164 }
165