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