xref: /aosp_15_r20/external/bcc/libbpf-tools/bitesize.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2020 Wenbo Zhang
3 //
4 // Based on bitesize(8) from BCC by Brendan Gregg.
5 // 16-Jun-2020   Wenbo Zhang   Created this.
6 #include <argp.h>
7 #include <signal.h>
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <time.h>
11 #include <bpf/libbpf.h>
12 #include <bpf/bpf.h>
13 #include "bitesize.h"
14 #include "bitesize.skel.h"
15 #include "trace_helpers.h"
16 
17 static struct env {
18 	char *disk;
19 	char *comm;
20 	int comm_len;
21 	time_t interval;
22 	bool timestamp;
23 	bool verbose;
24 	int times;
25 } env = {
26 	.interval = 99999999,
27 	.times = 99999999,
28 };
29 
30 static volatile bool exiting;
31 
32 const char *argp_program_version = "bitesize 0.1";
33 const char *argp_program_bug_address =
34 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
35 const char argp_program_doc[] =
36 "Summarize block device I/O size as a histogram.\n"
37 "\n"
38 "USAGE: bitesize [--help] [-T] [-c COMM] [-d DISK] [interval] [count]\n"
39 "\n"
40 "EXAMPLES:\n"
41 "    bitesize              # summarize block I/O latency as a histogram\n"
42 "    bitesize 1 10         # print 1 second summaries, 10 times\n"
43 "    bitesize -T 1         # 1s summaries with timestamps\n"
44 "    bitesize -c fio       # trace fio only\n";
45 
46 static const struct argp_option opts[] = {
47 	{ "timestamp", 'T', NULL, 0, "Include timestamp on output" },
48 	{ "comm",  'c', "COMM",  0, "Trace this comm only" },
49 	{ "disk",  'd', "DISK",  0, "Trace this disk only" },
50 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
51 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
52 	{},
53 };
54 
parse_arg(int key,char * arg,struct argp_state * state)55 static error_t parse_arg(int key, char *arg, struct argp_state *state)
56 {
57 	static int pos_args, len;
58 
59 	switch (key) {
60 	case 'h':
61 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
62 		break;
63 	case 'v':
64 		env.verbose = true;
65 		break;
66 	case 'c':
67 		env.comm = arg;
68 		len = strlen(arg) + 1;
69 		env.comm_len = len > TASK_COMM_LEN ? TASK_COMM_LEN : len;
70 		break;
71 	case 'd':
72 		env.disk = arg;
73 		if (strlen(arg) + 1 > DISK_NAME_LEN) {
74 			fprintf(stderr, "invaild disk name: too long\n");
75 			argp_usage(state);
76 		}
77 		break;
78 	case 'T':
79 		env.timestamp = true;
80 		break;
81 	case ARGP_KEY_ARG:
82 		errno = 0;
83 		if (pos_args == 0) {
84 			env.interval = strtol(arg, NULL, 10);
85 			if (errno) {
86 				fprintf(stderr, "invalid internal\n");
87 				argp_usage(state);
88 			}
89 		} else if (pos_args == 1) {
90 			env.times = strtol(arg, NULL, 10);
91 			if (errno) {
92 				fprintf(stderr, "invalid times\n");
93 				argp_usage(state);
94 			}
95 		} else {
96 			fprintf(stderr,
97 				"unrecognized positional argument: %s\n", arg);
98 			argp_usage(state);
99 		}
100 		pos_args++;
101 		break;
102 	default:
103 		return ARGP_ERR_UNKNOWN;
104 	}
105 	return 0;
106 }
107 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)108 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
109 {
110 	if (level == LIBBPF_DEBUG && !env.verbose)
111 		return 0;
112 	return vfprintf(stderr, format, args);
113 }
114 
sig_handler(int sig)115 static void sig_handler(int sig)
116 {
117 	exiting = true;
118 }
119 
print_log2_hists(int fd)120 static int print_log2_hists(int fd)
121 {
122 	struct hist_key lookup_key, next_key;
123 	struct hist hist;
124 	int err;
125 
126 	memset(lookup_key.comm, '?', sizeof(lookup_key.comm));
127 	while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
128 		err = bpf_map_lookup_elem(fd, &next_key, &hist);
129 		if (err < 0) {
130 			fprintf(stderr, "failed to lookup hist: %d\n", err);
131 			return -1;
132 		}
133 		printf("\nProcess Name = %s\n", next_key.comm);
134 		print_log2_hist(hist.slots, MAX_SLOTS, "Kbytes");
135 		lookup_key = next_key;
136 	}
137 
138 	memset(lookup_key.comm, '?', sizeof(lookup_key.comm));
139 	while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
140 		err = bpf_map_delete_elem(fd, &next_key);
141 		if (err < 0) {
142 			fprintf(stderr, "failed to cleanup hist : %d\n", err);
143 			return -1;
144 		}
145 		lookup_key = next_key;
146 	}
147 
148 	return 0;
149 }
150 
main(int argc,char ** argv)151 int main(int argc, char **argv)
152 {
153 	struct partitions *partitions = NULL;
154 	const struct partition *partition;
155 	static const struct argp argp = {
156 		.options = opts,
157 		.parser = parse_arg,
158 		.doc = argp_program_doc,
159 	};
160 	struct bitesize_bpf *obj;
161 	struct tm *tm;
162 	char ts[32];
163 	int fd, err;
164 	time_t t;
165 
166 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
167 	if (err)
168 		return err;
169 
170 	libbpf_set_print(libbpf_print_fn);
171 
172 	obj = bitesize_bpf__open();
173 	if (!obj) {
174 		fprintf(stderr, "failed to open BPF object\n");
175 		return 1;
176 	}
177 
178 	partitions = partitions__load();
179 	if (!partitions) {
180 		fprintf(stderr, "failed to load partitions info\n");
181 		goto cleanup;
182 	}
183 
184 	/* initialize global data (filtering options) */
185 	if (env.comm)
186 		strncpy((char*)obj->rodata->targ_comm, env.comm, env.comm_len);
187 	if (env.disk) {
188 		partition = partitions__get_by_name(partitions, env.disk);
189 		if (!partition) {
190 			fprintf(stderr, "invaild partition name: not exist\n");
191 			goto cleanup;
192 		}
193 		obj->rodata->filter_dev = true;
194 		obj->rodata->targ_dev = partition->dev;
195 	}
196 
197 	err = bitesize_bpf__load(obj);
198 	if (err) {
199 		fprintf(stderr, "failed to load BPF object: %d\n", err);
200 		goto cleanup;
201 	}
202 
203 	err = bitesize_bpf__attach(obj);
204 	if (err) {
205 		fprintf(stderr, "failed to attach BPF programs\n");
206 		goto cleanup;
207 	}
208 
209 	fd = bpf_map__fd(obj->maps.hists);
210 
211 	signal(SIGINT, sig_handler);
212 
213 	printf("Tracing block device I/O... Hit Ctrl-C to end.\n");
214 
215 	/* main: poll */
216 	while (1) {
217 		sleep(env.interval);
218 		printf("\n");
219 
220 		if (env.timestamp) {
221 			time(&t);
222 			tm = localtime(&t);
223 			strftime(ts, sizeof(ts), "%H:%M:%S", tm);
224 			printf("%-8s\n", ts);
225 		}
226 
227 		err = print_log2_hists(fd);
228 		if (err)
229 			break;
230 
231 		if (exiting || --env.times == 0)
232 			break;
233 	}
234 
235 cleanup:
236 	bitesize_bpf__destroy(obj);
237 	partitions__free(partitions);
238 
239 	return err != 0;
240 }
241