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