1*4882a593Smuzhiyun // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2*4882a593Smuzhiyun // Copyright (c) 2019 Facebook
3*4882a593Smuzhiyun #include <argp.h>
4*4882a593Smuzhiyun #include <stdio.h>
5*4882a593Smuzhiyun #include <stdlib.h>
6*4882a593Smuzhiyun #include <string.h>
7*4882a593Smuzhiyun #include <sys/resource.h>
8*4882a593Smuzhiyun #include <time.h>
9*4882a593Smuzhiyun #include <bpf/libbpf.h>
10*4882a593Smuzhiyun #include <bpf/bpf.h>
11*4882a593Smuzhiyun #include "runqslower.h"
12*4882a593Smuzhiyun #include "runqslower.skel.h"
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun struct env {
15*4882a593Smuzhiyun pid_t pid;
16*4882a593Smuzhiyun __u64 min_us;
17*4882a593Smuzhiyun bool verbose;
18*4882a593Smuzhiyun } env = {
19*4882a593Smuzhiyun .min_us = 10000,
20*4882a593Smuzhiyun };
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun const char *argp_program_version = "runqslower 0.1";
23*4882a593Smuzhiyun const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
24*4882a593Smuzhiyun const char argp_program_doc[] =
25*4882a593Smuzhiyun "runqslower Trace long process scheduling delays.\n"
26*4882a593Smuzhiyun " For Linux, uses eBPF, BPF CO-RE, libbpf, BTF.\n"
27*4882a593Smuzhiyun "\n"
28*4882a593Smuzhiyun "This script traces high scheduling delays between tasks being\n"
29*4882a593Smuzhiyun "ready to run and them running on CPU after that.\n"
30*4882a593Smuzhiyun "\n"
31*4882a593Smuzhiyun "USAGE: runqslower [-p PID] [min_us]\n"
32*4882a593Smuzhiyun "\n"
33*4882a593Smuzhiyun "EXAMPLES:\n"
34*4882a593Smuzhiyun " runqslower # trace run queue latency higher than 10000 us (default)\n"
35*4882a593Smuzhiyun " runqslower 1000 # trace run queue latency higher than 1000 us\n"
36*4882a593Smuzhiyun " runqslower -p 123 # trace pid 123 only\n";
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun static const struct argp_option opts[] = {
39*4882a593Smuzhiyun { "pid", 'p', "PID", 0, "Process PID to trace"},
40*4882a593Smuzhiyun { "verbose", 'v', NULL, 0, "Verbose debug output" },
41*4882a593Smuzhiyun {},
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun
parse_arg(int key,char * arg,struct argp_state * state)44*4882a593Smuzhiyun static error_t parse_arg(int key, char *arg, struct argp_state *state)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun static int pos_args;
47*4882a593Smuzhiyun int pid;
48*4882a593Smuzhiyun long long min_us;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun switch (key) {
51*4882a593Smuzhiyun case 'v':
52*4882a593Smuzhiyun env.verbose = true;
53*4882a593Smuzhiyun break;
54*4882a593Smuzhiyun case 'p':
55*4882a593Smuzhiyun errno = 0;
56*4882a593Smuzhiyun pid = strtol(arg, NULL, 10);
57*4882a593Smuzhiyun if (errno || pid <= 0) {
58*4882a593Smuzhiyun fprintf(stderr, "Invalid PID: %s\n", arg);
59*4882a593Smuzhiyun argp_usage(state);
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun env.pid = pid;
62*4882a593Smuzhiyun break;
63*4882a593Smuzhiyun case ARGP_KEY_ARG:
64*4882a593Smuzhiyun if (pos_args++) {
65*4882a593Smuzhiyun fprintf(stderr,
66*4882a593Smuzhiyun "Unrecognized positional argument: %s\n", arg);
67*4882a593Smuzhiyun argp_usage(state);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun errno = 0;
70*4882a593Smuzhiyun min_us = strtoll(arg, NULL, 10);
71*4882a593Smuzhiyun if (errno || min_us <= 0) {
72*4882a593Smuzhiyun fprintf(stderr, "Invalid delay (in us): %s\n", arg);
73*4882a593Smuzhiyun argp_usage(state);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun env.min_us = min_us;
76*4882a593Smuzhiyun break;
77*4882a593Smuzhiyun default:
78*4882a593Smuzhiyun return ARGP_ERR_UNKNOWN;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun return 0;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)83*4882a593Smuzhiyun int libbpf_print_fn(enum libbpf_print_level level,
84*4882a593Smuzhiyun const char *format, va_list args)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun if (level == LIBBPF_DEBUG && !env.verbose)
87*4882a593Smuzhiyun return 0;
88*4882a593Smuzhiyun return vfprintf(stderr, format, args);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
bump_memlock_rlimit(void)91*4882a593Smuzhiyun static int bump_memlock_rlimit(void)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun struct rlimit rlim_new = {
94*4882a593Smuzhiyun .rlim_cur = RLIM_INFINITY,
95*4882a593Smuzhiyun .rlim_max = RLIM_INFINITY,
96*4882a593Smuzhiyun };
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)101*4882a593Smuzhiyun void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun const struct event *e = data;
104*4882a593Smuzhiyun struct tm *tm;
105*4882a593Smuzhiyun char ts[32];
106*4882a593Smuzhiyun time_t t;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun time(&t);
109*4882a593Smuzhiyun tm = localtime(&t);
110*4882a593Smuzhiyun strftime(ts, sizeof(ts), "%H:%M:%S", tm);
111*4882a593Smuzhiyun printf("%-8s %-16s %-6d %14llu\n", ts, e->task, e->pid, e->delta_us);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)114*4882a593Smuzhiyun void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun printf("Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
main(int argc,char ** argv)119*4882a593Smuzhiyun int main(int argc, char **argv)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun static const struct argp argp = {
122*4882a593Smuzhiyun .options = opts,
123*4882a593Smuzhiyun .parser = parse_arg,
124*4882a593Smuzhiyun .doc = argp_program_doc,
125*4882a593Smuzhiyun };
126*4882a593Smuzhiyun struct perf_buffer_opts pb_opts;
127*4882a593Smuzhiyun struct perf_buffer *pb = NULL;
128*4882a593Smuzhiyun struct runqslower_bpf *obj;
129*4882a593Smuzhiyun int err;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
132*4882a593Smuzhiyun if (err)
133*4882a593Smuzhiyun return err;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun libbpf_set_print(libbpf_print_fn);
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun err = bump_memlock_rlimit();
138*4882a593Smuzhiyun if (err) {
139*4882a593Smuzhiyun fprintf(stderr, "failed to increase rlimit: %d", err);
140*4882a593Smuzhiyun return 1;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun obj = runqslower_bpf__open();
144*4882a593Smuzhiyun if (!obj) {
145*4882a593Smuzhiyun fprintf(stderr, "failed to open and/or load BPF object\n");
146*4882a593Smuzhiyun return 1;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun /* initialize global data (filtering options) */
150*4882a593Smuzhiyun obj->rodata->targ_pid = env.pid;
151*4882a593Smuzhiyun obj->rodata->min_us = env.min_us;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun err = runqslower_bpf__load(obj);
154*4882a593Smuzhiyun if (err) {
155*4882a593Smuzhiyun fprintf(stderr, "failed to load BPF object: %d\n", err);
156*4882a593Smuzhiyun goto cleanup;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun err = runqslower_bpf__attach(obj);
160*4882a593Smuzhiyun if (err) {
161*4882a593Smuzhiyun fprintf(stderr, "failed to attach BPF programs\n");
162*4882a593Smuzhiyun goto cleanup;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun printf("Tracing run queue latency higher than %llu us\n", env.min_us);
166*4882a593Smuzhiyun printf("%-8s %-16s %-6s %14s\n", "TIME", "COMM", "PID", "LAT(us)");
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun pb_opts.sample_cb = handle_event;
169*4882a593Smuzhiyun pb_opts.lost_cb = handle_lost_events;
170*4882a593Smuzhiyun pb = perf_buffer__new(bpf_map__fd(obj->maps.events), 64, &pb_opts);
171*4882a593Smuzhiyun err = libbpf_get_error(pb);
172*4882a593Smuzhiyun if (err) {
173*4882a593Smuzhiyun pb = NULL;
174*4882a593Smuzhiyun fprintf(stderr, "failed to open perf buffer: %d\n", err);
175*4882a593Smuzhiyun goto cleanup;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun while ((err = perf_buffer__poll(pb, 100)) >= 0)
179*4882a593Smuzhiyun ;
180*4882a593Smuzhiyun printf("Error polling perf buffer: %d\n", err);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun cleanup:
183*4882a593Smuzhiyun perf_buffer__free(pb);
184*4882a593Smuzhiyun runqslower_bpf__destroy(obj);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun return err != 0;
187*4882a593Smuzhiyun }
188