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