xref: /aosp_15_r20/external/bcc/libbpf-tools/biostacks.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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 
has_block_io_tracepoints(void)131 static bool has_block_io_tracepoints(void)
132 {
133 	return tracepoint_exists("block", "block_io_start") &&
134 		tracepoint_exists("block", "block_io_done");
135 }
136 
disable_block_io_tracepoints(struct biostacks_bpf * obj)137 static void disable_block_io_tracepoints(struct biostacks_bpf *obj)
138 {
139 	bpf_program__set_autoload(obj->progs.block_io_start, false);
140 	bpf_program__set_autoload(obj->progs.block_io_done, false);
141 }
142 
disable_blk_account_io_fentry(struct biostacks_bpf * obj)143 static void disable_blk_account_io_fentry(struct biostacks_bpf *obj)
144 {
145 	bpf_program__set_autoload(obj->progs.blk_account_io_start, false);
146 	bpf_program__set_autoload(obj->progs.blk_account_io_done, false);
147 }
148 
blk_account_io_set_attach_target(struct biostacks_bpf * obj)149 static void blk_account_io_set_attach_target(struct biostacks_bpf *obj)
150 {
151 	if (fentry_can_attach("blk_account_io_start", NULL)) {
152 		bpf_program__set_attach_target(obj->progs.blk_account_io_start,
153 					       0, "blk_account_io_start");
154 		bpf_program__set_attach_target(obj->progs.blk_account_io_done,
155 					       0, "blk_account_io_done");
156 	} else {
157 		bpf_program__set_attach_target(obj->progs.blk_account_io_start,
158 					       0, "__blk_account_io_start");
159 		bpf_program__set_attach_target(obj->progs.blk_account_io_done,
160 					       0, "__blk_account_io_done");
161 	}
162 }
163 
main(int argc,char ** argv)164 int main(int argc, char **argv)
165 {
166 	struct partitions *partitions = NULL;
167 	const struct partition *partition;
168 	static const struct argp argp = {
169 		.options = opts,
170 		.parser = parse_arg,
171 		.doc = argp_program_doc,
172 	};
173 	struct ksyms *ksyms = NULL;
174 	struct biostacks_bpf *obj;
175 	int err;
176 
177 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
178 	if (err)
179 		return err;
180 
181 	libbpf_set_print(libbpf_print_fn);
182 
183 	obj = biostacks_bpf__open();
184 	if (!obj) {
185 		fprintf(stderr, "failed to open BPF object\n");
186 		return 1;
187 	}
188 
189 	partitions = partitions__load();
190 	if (!partitions) {
191 		fprintf(stderr, "failed to load partitions info\n");
192 		goto cleanup;
193 	}
194 
195 	/* initialize global data (filtering options) */
196 	if (env.disk) {
197 		partition = partitions__get_by_name(partitions, env.disk);
198 		if (!partition) {
199 			fprintf(stderr, "invaild partition name: not exist\n");
200 			goto cleanup;
201 		}
202 		obj->rodata->filter_dev = true;
203 		obj->rodata->targ_dev = partition->dev;
204 	}
205 
206 	obj->rodata->targ_ms = env.milliseconds;
207 
208 	if (has_block_io_tracepoints())
209 		disable_blk_account_io_fentry(obj);
210 	else {
211 		disable_block_io_tracepoints(obj);
212 		blk_account_io_set_attach_target(obj);
213 	}
214 
215 	ksyms = ksyms__load();
216 	if (!ksyms) {
217 		fprintf(stderr, "failed to load kallsyms\n");
218 		goto cleanup;
219 	}
220 	if (!ksyms__get_symbol(ksyms, "blk_account_io_merge_bio"))
221 		bpf_program__set_autoload(obj->progs.blk_account_io_merge_bio, false);
222 
223 	err = biostacks_bpf__load(obj);
224 	if (err) {
225 		fprintf(stderr, "failed to load BPF object: %d\n", err);
226 		goto cleanup;
227 	}
228 
229 	err = biostacks_bpf__attach(obj);
230 	if (err) {
231 		fprintf(stderr, "failed to attach BPF programs: %d\n", err);
232 		goto cleanup;
233 	}
234 
235 	signal(SIGINT, sig_handler);
236 
237 	printf("Tracing block I/O with init stacks. Hit Ctrl-C to end.\n");
238 	sleep(env.duration);
239 	print_map(ksyms, partitions, bpf_map__fd(obj->maps.hists));
240 
241 cleanup:
242 	biostacks_bpf__destroy(obj);
243 	ksyms__free(ksyms);
244 	partitions__free(partitions);
245 
246 	return err != 0;
247 }
248