xref: /aosp_15_r20/external/bcc/libbpf-tools/mdflush.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2 
3 /*
4  * mdflush	Trace md flush events.
5  *
6  * Copyright (c) 2021~2022 Hengqi Chen
7  *
8  * Based on mdflush(8) from BCC by Brendan Gregg.
9  * 08-Nov-2021   Hengqi Chen   Created this.
10  */
11 #include <argp.h>
12 #include <errno.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <time.h>
16 
17 #include <bpf/libbpf.h>
18 #include <bpf/bpf.h>
19 #include "mdflush.h"
20 #include "mdflush.skel.h"
21 #include "trace_helpers.h"
22 
23 #define PERF_BUFFER_PAGES	16
24 #define PERF_POLL_TIMEOUT_MS	100
25 #define warn(...) fprintf(stderr, __VA_ARGS__)
26 
27 static volatile sig_atomic_t exiting = 0;
28 static bool verbose = false;
29 
30 const char *argp_program_version = "mdflush 0.1";
31 const char *argp_program_bug_address =
32 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
33 const char argp_program_doc[] =
34 "Trace md flush events.\n"
35 "\n"
36 "USAGE: mdflush\n";
37 
38 static const struct argp_option opts[] = {
39 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
40 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
41 	{},
42 };
43 
parse_arg(int key,char * arg,struct argp_state * state)44 static error_t parse_arg(int key, char *arg, struct argp_state *state)
45 {
46 	switch (key) {
47 	case 'h':
48 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
49 		break;
50 	case 'v':
51 		verbose = true;
52 		break;
53 	default:
54 		return ARGP_ERR_UNKNOWN;
55 	}
56 	return 0;
57 }
58 
sig_int(int signo)59 static void sig_int(int signo)
60 {
61 	exiting = 1;
62 }
63 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)64 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
65 {
66 	if (level == LIBBPF_DEBUG && !verbose)
67 		return 0;
68 	return vfprintf(stderr, format, args);
69 }
70 
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)71 static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
72 {
73 	struct event *e = data;
74 	time_t t;
75 	struct tm *tm;
76 	char ts[32];
77 
78 	time(&t);
79 	tm = localtime(&t);
80 	strftime(ts, sizeof(ts), "%H:%M:%S", tm);
81 	printf("%-8s %-7d %-16s %-s\n",
82 	       ts, e->pid, e->comm, e->disk);
83 }
84 
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)85 static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
86 {
87 	warn("lost %llu events on CPU #%d\n", lost_cnt, cpu);
88 }
89 
main(int argc,char ** argv)90 int main(int argc, char **argv)
91 {
92 	static const struct argp argp = {
93 		.options = opts,
94 		.parser = parse_arg,
95 		.doc = argp_program_doc,
96 	};
97 	struct perf_buffer *pb = NULL;
98 	struct mdflush_bpf *obj;
99 	int err;
100 
101 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
102 	if (err)
103 		return err;
104 
105 	libbpf_set_print(libbpf_print_fn);
106 
107 	obj = mdflush_bpf__open();
108 	if (!obj) {
109 		warn("failed to open BPF object\n");
110 		return 1;
111 	}
112 
113 	if (fentry_can_attach("md_flush_request", NULL))
114 		bpf_program__set_autoload(obj->progs.kprobe_md_flush_request, false);
115 	else
116 		bpf_program__set_autoload(obj->progs.md_flush_request, false);
117 
118 	err = mdflush_bpf__load(obj);
119 	if (err) {
120 		warn("failed to load BPF object: %d\n", err);
121 		goto cleanup;
122 	}
123 
124 	err = mdflush_bpf__attach(obj);
125 	if (err) {
126 		warn("failed to attach BPF programs: %d\n", err);
127 		goto cleanup;
128 	}
129 
130 	pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
131 			      handle_event, handle_lost_events, NULL, NULL);
132 	if (!pb) {
133 		warn("failed to open perf buffer: %d\n", err);
134 		goto cleanup;
135 	}
136 
137 	if (signal(SIGINT, sig_int) == SIG_ERR) {
138 		warn("can't set signal handler: %s\n", strerror(errno));
139 		err = 1;
140 		goto cleanup;
141 	}
142 
143 	printf("Tracing md flush requests... Hit Ctrl-C to end.\n");
144 	printf("%-8s %-7s %-16s %-s\n",
145 	       "TIME", "PID", "COMM", "DEVICE");
146 
147 	while (!exiting) {
148 		err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
149 		if (err < 0 && err != -EINTR) {
150 			warn("error polling perf buffer: %s\n", strerror(-err));
151 			goto cleanup;
152 		}
153 		/* reset err to return 0 if exiting */
154 		err = 0;
155 	}
156 
157 cleanup:
158 	perf_buffer__free(pb);
159 	mdflush_bpf__destroy(obj);
160 
161 	return err != 0;
162 }
163