xref: /aosp_15_r20/external/bcc/libbpf-tools/runqslower.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2019 Facebook
3 //
4 // Based on runqslower(8) from BCC by Ivan Babrou.
5 // 11-Feb-2020   Andrii Nakryiko   Created this.
6 #include <argp.h>
7 #include <signal.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 #include <bpf/libbpf.h>
13 #include <bpf/bpf.h>
14 #include "runqslower.h"
15 #include "runqslower.skel.h"
16 #include "trace_helpers.h"
17 
18 static volatile sig_atomic_t exiting = 0;
19 
20 struct env {
21 	pid_t pid;
22 	pid_t tid;
23 	__u64 min_us;
24 	bool previous;
25 	bool verbose;
26 } env = {
27 	.min_us = 10000,
28 };
29 
30 const char *argp_program_version = "runqslower 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 high run queue latency.\n"
35 "\n"
36 "USAGE: runqslower [--help] [-p PID] [-t TID] [-P] [min_us]\n"
37 "\n"
38 "EXAMPLES:\n"
39 "    runqslower         # trace latency higher than 10000 us (default)\n"
40 "    runqslower 1000    # trace latency higher than 1000 us\n"
41 "    runqslower -p 123  # trace pid 123\n"
42 "    runqslower -t 123  # trace tid 123 (use for threads only)\n"
43 "    runqslower -P      # also show previous task name and TID\n";
44 
45 static const struct argp_option opts[] = {
46 	{ "pid", 'p', "PID", 0, "Process PID to trace"},
47 	{ "tid", 't', "TID", 0, "Thread TID to trace"},
48 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
49 	{ "previous", 'P', NULL, 0, "also show previous task name and TID" },
50 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
51 	{},
52 };
53 
parse_arg(int key,char * arg,struct argp_state * state)54 static error_t parse_arg(int key, char *arg, struct argp_state *state)
55 {
56 	static int pos_args;
57 	int pid;
58 	long long min_us;
59 
60 	switch (key) {
61 	case 'h':
62 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
63 		break;
64 	case 'v':
65 		env.verbose = true;
66 		break;
67 	case 'P':
68 		env.previous = true;
69 		break;
70 	case 'p':
71 		errno = 0;
72 		pid = strtol(arg, NULL, 10);
73 		if (errno || pid <= 0) {
74 			fprintf(stderr, "Invalid PID: %s\n", arg);
75 			argp_usage(state);
76 		}
77 		env.pid = pid;
78 		break;
79 	case 't':
80 		errno = 0;
81 		pid = strtol(arg, NULL, 10);
82 		if (errno || pid <= 0) {
83 			fprintf(stderr, "Invalid TID: %s\n", arg);
84 			argp_usage(state);
85 		}
86 		env.tid = pid;
87 		break;
88 	case ARGP_KEY_ARG:
89 		if (pos_args++) {
90 			fprintf(stderr,
91 				"Unrecognized positional argument: %s\n", arg);
92 			argp_usage(state);
93 		}
94 		errno = 0;
95 		min_us = strtoll(arg, NULL, 10);
96 		if (errno || min_us <= 0) {
97 			fprintf(stderr, "Invalid delay (in us): %s\n", arg);
98 			argp_usage(state);
99 		}
100 		env.min_us = min_us;
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_int(int signo)115 static void sig_int(int signo)
116 {
117 	exiting = 1;
118 }
119 
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)120 void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
121 {
122 	const struct event *e = data;
123 	struct tm *tm;
124 	char ts[32];
125 	time_t t;
126 
127 	time(&t);
128 	tm = localtime(&t);
129 	strftime(ts, sizeof(ts), "%H:%M:%S", tm);
130 	if (env.previous)
131 		printf("%-8s %-16s %-6d %14llu %-16s %-6d\n", ts, e->task, e->pid, e->delta_us, e->prev_task, e->prev_pid);
132 	else
133 		printf("%-8s %-16s %-6d %14llu\n", ts, e->task, e->pid, e->delta_us);
134 }
135 
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)136 void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
137 {
138 	printf("Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
139 }
140 
main(int argc,char ** argv)141 int main(int argc, char **argv)
142 {
143 	static const struct argp argp = {
144 		.options = opts,
145 		.parser = parse_arg,
146 		.doc = argp_program_doc,
147 	};
148 	struct perf_buffer *pb = NULL;
149 	struct runqslower_bpf *obj;
150 	int err;
151 
152 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
153 	if (err)
154 		return err;
155 
156 	libbpf_set_print(libbpf_print_fn);
157 
158 	obj = runqslower_bpf__open();
159 	if (!obj) {
160 		fprintf(stderr, "failed to open BPF object\n");
161 		return 1;
162 	}
163 
164 	/* initialize global data (filtering options) */
165 	obj->rodata->targ_tgid = env.pid;
166 	obj->rodata->targ_pid = env.tid;
167 	obj->rodata->min_us = env.min_us;
168 
169 	if (probe_tp_btf("sched_wakeup")) {
170 		bpf_program__set_autoload(obj->progs.handle_sched_wakeup, false);
171 		bpf_program__set_autoload(obj->progs.handle_sched_wakeup_new, false);
172 		bpf_program__set_autoload(obj->progs.handle_sched_switch, false);
173 	} else {
174 		bpf_program__set_autoload(obj->progs.sched_wakeup, false);
175 		bpf_program__set_autoload(obj->progs.sched_wakeup_new, false);
176 		bpf_program__set_autoload(obj->progs.sched_switch, false);
177 	}
178 
179 	err = runqslower_bpf__load(obj);
180 	if (err) {
181 		fprintf(stderr, "failed to load BPF object: %d\n", err);
182 		goto cleanup;
183 	}
184 
185 	err = runqslower_bpf__attach(obj);
186 	if (err) {
187 		fprintf(stderr, "failed to attach BPF programs\n");
188 		goto cleanup;
189 	}
190 
191 	printf("Tracing run queue latency higher than %llu us\n", env.min_us);
192 	if (env.previous)
193 		printf("%-8s %-16s %-6s %14s %-16s %-6s\n", "TIME", "COMM", "TID", "LAT(us)", "PREV COMM", "PREV TID");
194 	else
195 		printf("%-8s %-16s %-6s %14s\n", "TIME", "COMM", "TID", "LAT(us)");
196 
197 	pb = perf_buffer__new(bpf_map__fd(obj->maps.events), 64,
198 			      handle_event, handle_lost_events, NULL, NULL);
199 	if (!pb) {
200 		err = -errno;
201 		fprintf(stderr, "failed to open perf buffer: %d\n", err);
202 		goto cleanup;
203 	}
204 
205 	if (signal(SIGINT, sig_int) == SIG_ERR) {
206 		fprintf(stderr, "can't set signal handler: %s\n", strerror(errno));
207 		err = 1;
208 		goto cleanup;
209 	}
210 
211 	while (!exiting) {
212 		err = perf_buffer__poll(pb, 100);
213 		if (err < 0 && err != -EINTR) {
214 			fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
215 			goto cleanup;
216 		}
217 		/* reset err to return 0 if exiting */
218 		err = 0;
219 	}
220 
221 cleanup:
222 	perf_buffer__free(pb);
223 	runqslower_bpf__destroy(obj);
224 
225 	return err != 0;
226 }
227