1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2020 Wenbo Zhang
3 //
4 // Based on biostacks(8) from BPF-Perf-Tools-Book by Brendan Gregg.
5 // 10-Aug-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 "biostacks.h"
13 #include "biostacks.skel.h"
14 #include "trace_helpers.h"
15
16 static struct env {
17 char *disk;
18 int duration;
19 bool milliseconds;
20 bool verbose;
21 } env = {
22 .duration = -1,
23 };
24
25 const char *argp_program_version = "biostacks 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 "Tracing block I/O with init stacks.\n"
30 "\n"
31 "USAGE: biostacks [--help] [-d DISK] [-m] [duration]\n"
32 "\n"
33 "EXAMPLES:\n"
34 " biostacks # trace block I/O with init stacks.\n"
35 " biostacks 1 # trace for 1 seconds only\n"
36 " biostacks -d sdc # trace sdc only\n";
37
38 static const struct argp_option opts[] = {
39 { "disk", 'd', "DISK", 0, "Trace this disk only" },
40 { "milliseconds", 'm', NULL, 0, "Millisecond histogram" },
41 { "verbose", 'v', NULL, 0, "Verbose debug output" },
42 { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
43 {},
44 };
45
parse_arg(int key,char * arg,struct argp_state * state)46 static error_t parse_arg(int key, char *arg, struct argp_state *state)
47 {
48 static int pos_args;
49
50 switch (key) {
51 case 'h':
52 argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
53 break;
54 case 'v':
55 env.verbose = true;
56 break;
57 case 'd':
58 env.disk = arg;
59 if (strlen(arg) + 1 > DISK_NAME_LEN) {
60 fprintf(stderr, "invaild disk name: too long\n");
61 argp_usage(state);
62 }
63 break;
64 case 'm':
65 env.milliseconds = true;
66 break;
67 case ARGP_KEY_ARG:
68 if (pos_args++) {
69 fprintf(stderr,
70 "unrecognized positional argument: %s\n", arg);
71 argp_usage(state);
72 }
73 errno = 0;
74 env.duration = strtoll(arg, NULL, 10);
75 if (errno || env.duration <= 0) {
76 fprintf(stderr, "invalid delay (in us): %s\n", arg);
77 argp_usage(state);
78 }
79 break;
80 default:
81 return ARGP_ERR_UNKNOWN;
82 }
83 return 0;
84 }
85
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)86 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
87 {
88 if (level == LIBBPF_DEBUG && !env.verbose)
89 return 0;
90 return vfprintf(stderr, format, args);
91 }
92
sig_handler(int sig)93 static void sig_handler(int sig)
94 {
95 }
96
97 static
print_map(struct ksyms * ksyms,struct partitions * partitions,int fd)98 void print_map(struct ksyms *ksyms, struct partitions *partitions, int fd)
99 {
100 const char *units = env.milliseconds ? "msecs" : "usecs";
101 struct rqinfo lookup_key = {}, next_key;
102 const struct partition *partition;
103 const struct ksym *ksym;
104 int num_stack, i, err;
105 struct hist hist;
106
107 while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
108 err = bpf_map_lookup_elem(fd, &next_key, &hist);
109 if (err < 0) {
110 fprintf(stderr, "failed to lookup hist: %d\n", err);
111 return;
112 }
113 partition = partitions__get_by_dev(partitions, next_key.dev);
114 printf("%-14.14s %-6d %-7s\n",
115 next_key.comm, next_key.pid,
116 partition ? partition->name : "Unknown");
117 num_stack = next_key.kern_stack_size /
118 sizeof(next_key.kern_stack[0]);
119 for (i = 0; i < num_stack; i++) {
120 ksym = ksyms__map_addr(ksyms, next_key.kern_stack[i]);
121 printf("%s\n", ksym ? ksym->name : "Unknown");
122 }
123 print_log2_hist(hist.slots, MAX_SLOTS, units);
124 printf("\n");
125 lookup_key = next_key;
126 }
127
128 return;
129 }
130
main(int argc,char ** argv)131 int main(int argc, char **argv)
132 {
133 struct partitions *partitions = NULL;
134 const struct partition *partition;
135 static const struct argp argp = {
136 .options = opts,
137 .parser = parse_arg,
138 .doc = argp_program_doc,
139 };
140 struct ksyms *ksyms = NULL;
141 struct biostacks_bpf *obj;
142 int err;
143
144 err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
145 if (err)
146 return err;
147
148 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
149 libbpf_set_print(libbpf_print_fn);
150
151 obj = biostacks_bpf__open();
152 if (!obj) {
153 fprintf(stderr, "failed to open BPF object\n");
154 return 1;
155 }
156
157 partitions = partitions__load();
158 if (!partitions) {
159 fprintf(stderr, "failed to load partitions info\n");
160 goto cleanup;
161 }
162
163 /* initialize global data (filtering options) */
164 if (env.disk) {
165 partition = partitions__get_by_name(partitions, env.disk);
166 if (!partition) {
167 fprintf(stderr, "invaild partition name: not exist\n");
168 goto cleanup;
169 }
170 obj->rodata->filter_dev = true;
171 obj->rodata->targ_dev = partition->dev;
172 }
173
174 obj->rodata->targ_ms = env.milliseconds;
175
176 err = biostacks_bpf__load(obj);
177 if (err) {
178 fprintf(stderr, "failed to load BPF object: %d\n", err);
179 goto cleanup;
180 }
181
182 obj->links.blk_account_io_start = bpf_program__attach(obj->progs.blk_account_io_start);
183 if (!obj->links.blk_account_io_start) {
184 err = -errno;
185 fprintf(stderr, "failed to attach blk_account_io_start: %s\n", strerror(-err));
186 goto cleanup;
187 }
188 ksyms = ksyms__load();
189 if (!ksyms) {
190 fprintf(stderr, "failed to load kallsyms\n");
191 goto cleanup;
192 }
193 if (ksyms__get_symbol(ksyms, "blk_account_io_merge_bio")) {
194 obj->links.blk_account_io_merge_bio =
195 bpf_program__attach(obj->progs.blk_account_io_merge_bio);
196 if (!obj->links.blk_account_io_merge_bio) {
197 err = -errno;
198 fprintf(stderr, "failed to attach blk_account_io_merge_bio: %s\n",
199 strerror(-err));
200 goto cleanup;
201 }
202 }
203 obj->links.blk_account_io_done = bpf_program__attach(obj->progs.blk_account_io_done);
204 if (!obj->links.blk_account_io_done) {
205 err = -errno;
206 fprintf(stderr, "failed to attach blk_account_io_done: %s\n",
207 strerror(-err));
208 goto cleanup;
209 }
210
211 signal(SIGINT, sig_handler);
212
213 printf("Tracing block I/O with init stacks. Hit Ctrl-C to end.\n");
214 sleep(env.duration);
215 print_map(ksyms, partitions, bpf_map__fd(obj->maps.hists));
216
217 cleanup:
218 biostacks_bpf__destroy(obj);
219 ksyms__free(ksyms);
220 partitions__free(partitions);
221
222 return err != 0;
223 }
224