Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
0002 // Copyright (c) 2019 Facebook
0003 #include <argp.h>
0004 #include <stdio.h>
0005 #include <stdlib.h>
0006 #include <string.h>
0007 #include <time.h>
0008 #include <bpf/libbpf.h>
0009 #include <bpf/bpf.h>
0010 #include "runqslower.h"
0011 #include "runqslower.skel.h"
0012 
0013 struct env {
0014     pid_t pid;
0015     __u64 min_us;
0016     bool verbose;
0017 } env = {
0018     .min_us = 10000,
0019 };
0020 
0021 const char *argp_program_version = "runqslower 0.1";
0022 const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
0023 const char argp_program_doc[] =
0024 "runqslower    Trace long process scheduling delays.\n"
0025 "              For Linux, uses eBPF, BPF CO-RE, libbpf, BTF.\n"
0026 "\n"
0027 "This script traces high scheduling delays between tasks being\n"
0028 "ready to run and them running on CPU after that.\n"
0029 "\n"
0030 "USAGE: runqslower [-p PID] [min_us]\n"
0031 "\n"
0032 "EXAMPLES:\n"
0033 "    runqslower         # trace run queue latency higher than 10000 us (default)\n"
0034 "    runqslower 1000    # trace run queue latency higher than 1000 us\n"
0035 "    runqslower -p 123  # trace pid 123 only\n";
0036 
0037 static const struct argp_option opts[] = {
0038     { "pid", 'p', "PID", 0, "Process PID to trace"},
0039     { "verbose", 'v', NULL, 0, "Verbose debug output" },
0040     {},
0041 };
0042 
0043 static error_t parse_arg(int key, char *arg, struct argp_state *state)
0044 {
0045     static int pos_args;
0046     int pid;
0047     long long min_us;
0048 
0049     switch (key) {
0050     case 'v':
0051         env.verbose = true;
0052         break;
0053     case 'p':
0054         errno = 0;
0055         pid = strtol(arg, NULL, 10);
0056         if (errno || pid <= 0) {
0057             fprintf(stderr, "Invalid PID: %s\n", arg);
0058             argp_usage(state);
0059         }
0060         env.pid = pid;
0061         break;
0062     case ARGP_KEY_ARG:
0063         if (pos_args++) {
0064             fprintf(stderr,
0065                 "Unrecognized positional argument: %s\n", arg);
0066             argp_usage(state);
0067         }
0068         errno = 0;
0069         min_us = strtoll(arg, NULL, 10);
0070         if (errno || min_us <= 0) {
0071             fprintf(stderr, "Invalid delay (in us): %s\n", arg);
0072             argp_usage(state);
0073         }
0074         env.min_us = min_us;
0075         break;
0076     default:
0077         return ARGP_ERR_UNKNOWN;
0078     }
0079     return 0;
0080 }
0081 
0082 int libbpf_print_fn(enum libbpf_print_level level,
0083             const char *format, va_list args)
0084 {
0085     if (level == LIBBPF_DEBUG && !env.verbose)
0086         return 0;
0087     return vfprintf(stderr, format, args);
0088 }
0089 
0090 void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
0091 {
0092     const struct runq_event *e = data;
0093     struct tm *tm;
0094     char ts[32];
0095     time_t t;
0096 
0097     time(&t);
0098     tm = localtime(&t);
0099     strftime(ts, sizeof(ts), "%H:%M:%S", tm);
0100     printf("%-8s %-16s %-6d %14llu\n", ts, e->task, e->pid, e->delta_us);
0101 }
0102 
0103 void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
0104 {
0105     printf("Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
0106 }
0107 
0108 int main(int argc, char **argv)
0109 {
0110     static const struct argp argp = {
0111         .options = opts,
0112         .parser = parse_arg,
0113         .doc = argp_program_doc,
0114     };
0115     struct perf_buffer *pb = NULL;
0116     struct runqslower_bpf *obj;
0117     int err;
0118 
0119     err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
0120     if (err)
0121         return err;
0122 
0123     libbpf_set_print(libbpf_print_fn);
0124 
0125     /* Use libbpf 1.0 API mode */
0126     libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
0127 
0128     obj = runqslower_bpf__open();
0129     if (!obj) {
0130         fprintf(stderr, "failed to open and/or load BPF object\n");
0131         return 1;
0132     }
0133 
0134     /* initialize global data (filtering options) */
0135     obj->rodata->targ_pid = env.pid;
0136     obj->rodata->min_us = env.min_us;
0137 
0138     err = runqslower_bpf__load(obj);
0139     if (err) {
0140         fprintf(stderr, "failed to load BPF object: %d\n", err);
0141         goto cleanup;
0142     }
0143 
0144     err = runqslower_bpf__attach(obj);
0145     if (err) {
0146         fprintf(stderr, "failed to attach BPF programs\n");
0147         goto cleanup;
0148     }
0149 
0150     printf("Tracing run queue latency higher than %llu us\n", env.min_us);
0151     printf("%-8s %-16s %-6s %14s\n", "TIME", "COMM", "PID", "LAT(us)");
0152 
0153     pb = perf_buffer__new(bpf_map__fd(obj->maps.events), 64,
0154                   handle_event, handle_lost_events, NULL, NULL);
0155     err = libbpf_get_error(pb);
0156     if (err) {
0157         pb = NULL;
0158         fprintf(stderr, "failed to open perf buffer: %d\n", err);
0159         goto cleanup;
0160     }
0161 
0162     while ((err = perf_buffer__poll(pb, 100)) >= 0)
0163         ;
0164     printf("Error polling perf buffer: %d\n", err);
0165 
0166 cleanup:
0167     perf_buffer__free(pb);
0168     runqslower_bpf__destroy(obj);
0169 
0170     return err != 0;
0171 }