• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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