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"
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_strict_mode(LIBBPF_STRICT_ALL);
113 libbpf_set_print(libbpf_print_fn);
114
115 obj = readahead_bpf__open();
116 if (!obj) {
117 fprintf(stderr, "failed to open BPF object\n");
118 return 1;
119 }
120
121 /*
122 * Starting from v5.10-rc1 (8238287), __do_page_cache_readahead has
123 * renamed to do_page_cache_ra. So we specify the function dynamically.
124 */
125 err = readahead__set_attach_target(obj->progs.do_page_cache_ra);
126 if (err)
127 goto cleanup;
128 err = readahead__set_attach_target(obj->progs.do_page_cache_ra_ret);
129 if (err)
130 goto cleanup;
131
132 err = readahead_bpf__load(obj);
133 if (err) {
134 fprintf(stderr, "failed to load BPF object\n");
135 goto cleanup;
136 }
137
138 if (!obj->bss) {
139 fprintf(stderr, "Memory-mapping BPF maps is supported starting from Linux 5.7, please upgrade.\n");
140 goto cleanup;
141 }
142
143 err = readahead_bpf__attach(obj);
144 if (err) {
145 fprintf(stderr, "failed to attach BPF programs\n");
146 goto cleanup;
147 }
148
149 signal(SIGINT, sig_handler);
150
151 printf("Tracing fs read-ahead ... Hit Ctrl-C to end.\n");
152
153 sleep(env.duration);
154 printf("\n");
155
156 histp = &obj->bss->hist;
157
158 printf("Readahead unused/total pages: %d/%d\n",
159 histp->unused, histp->total);
160 print_log2_hist(histp->slots, MAX_SLOTS, "msecs");
161
162 cleanup:
163 readahead_bpf__destroy(obj);
164 return err != 0;
165 }
166