runqslower.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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 <time.h>
  8. #include <bpf/libbpf.h>
  9. #include <bpf/bpf.h>
  10. #include "runqslower.h"
  11. #include "runqslower.skel.h"
  12. struct env {
  13. pid_t pid;
  14. __u64 min_us;
  15. bool verbose;
  16. } env = {
  17. .min_us = 10000,
  18. };
  19. const char *argp_program_version = "runqslower 0.1";
  20. const char *argp_program_bug_address = "<[email protected]>";
  21. const char argp_program_doc[] =
  22. "runqslower Trace long process scheduling delays.\n"
  23. " For Linux, uses eBPF, BPF CO-RE, libbpf, BTF.\n"
  24. "\n"
  25. "This script traces high scheduling delays between tasks being\n"
  26. "ready to run and them running on CPU after that.\n"
  27. "\n"
  28. "USAGE: runqslower [-p PID] [min_us]\n"
  29. "\n"
  30. "EXAMPLES:\n"
  31. " runqslower # trace run queue latency higher than 10000 us (default)\n"
  32. " runqslower 1000 # trace run queue latency higher than 1000 us\n"
  33. " runqslower -p 123 # trace pid 123 only\n";
  34. static const struct argp_option opts[] = {
  35. { "pid", 'p', "PID", 0, "Process PID to trace"},
  36. { "verbose", 'v', NULL, 0, "Verbose debug output" },
  37. {},
  38. };
  39. static error_t parse_arg(int key, char *arg, struct argp_state *state)
  40. {
  41. static int pos_args;
  42. int pid;
  43. long long min_us;
  44. switch (key) {
  45. case 'v':
  46. env.verbose = true;
  47. break;
  48. case 'p':
  49. errno = 0;
  50. pid = strtol(arg, NULL, 10);
  51. if (errno || pid <= 0) {
  52. fprintf(stderr, "Invalid PID: %s\n", arg);
  53. argp_usage(state);
  54. }
  55. env.pid = pid;
  56. break;
  57. case ARGP_KEY_ARG:
  58. if (pos_args++) {
  59. fprintf(stderr,
  60. "Unrecognized positional argument: %s\n", arg);
  61. argp_usage(state);
  62. }
  63. errno = 0;
  64. min_us = strtoll(arg, NULL, 10);
  65. if (errno || min_us <= 0) {
  66. fprintf(stderr, "Invalid delay (in us): %s\n", arg);
  67. argp_usage(state);
  68. }
  69. env.min_us = min_us;
  70. break;
  71. default:
  72. return ARGP_ERR_UNKNOWN;
  73. }
  74. return 0;
  75. }
  76. int libbpf_print_fn(enum libbpf_print_level level,
  77. const char *format, va_list args)
  78. {
  79. if (level == LIBBPF_DEBUG && !env.verbose)
  80. return 0;
  81. return vfprintf(stderr, format, args);
  82. }
  83. void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
  84. {
  85. const struct runq_event *e = data;
  86. struct tm *tm;
  87. char ts[32];
  88. time_t t;
  89. time(&t);
  90. tm = localtime(&t);
  91. strftime(ts, sizeof(ts), "%H:%M:%S", tm);
  92. printf("%-8s %-16s %-6d %14llu\n", ts, e->task, e->pid, e->delta_us);
  93. }
  94. void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
  95. {
  96. printf("Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
  97. }
  98. int main(int argc, char **argv)
  99. {
  100. static const struct argp argp = {
  101. .options = opts,
  102. .parser = parse_arg,
  103. .doc = argp_program_doc,
  104. };
  105. struct perf_buffer *pb = NULL;
  106. struct runqslower_bpf *obj;
  107. int err;
  108. err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
  109. if (err)
  110. return err;
  111. libbpf_set_print(libbpf_print_fn);
  112. /* Use libbpf 1.0 API mode */
  113. libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
  114. obj = runqslower_bpf__open();
  115. if (!obj) {
  116. fprintf(stderr, "failed to open and/or load BPF object\n");
  117. return 1;
  118. }
  119. /* initialize global data (filtering options) */
  120. obj->rodata->targ_pid = env.pid;
  121. obj->rodata->min_us = env.min_us;
  122. err = runqslower_bpf__load(obj);
  123. if (err) {
  124. fprintf(stderr, "failed to load BPF object: %d\n", err);
  125. goto cleanup;
  126. }
  127. err = runqslower_bpf__attach(obj);
  128. if (err) {
  129. fprintf(stderr, "failed to attach BPF programs\n");
  130. goto cleanup;
  131. }
  132. printf("Tracing run queue latency higher than %llu us\n", env.min_us);
  133. printf("%-8s %-16s %-6s %14s\n", "TIME", "COMM", "PID", "LAT(us)");
  134. pb = perf_buffer__new(bpf_map__fd(obj->maps.events), 64,
  135. handle_event, handle_lost_events, NULL, NULL);
  136. err = libbpf_get_error(pb);
  137. if (err) {
  138. pb = NULL;
  139. fprintf(stderr, "failed to open perf buffer: %d\n", err);
  140. goto cleanup;
  141. }
  142. while ((err = perf_buffer__poll(pb, 100)) >= 0)
  143. ;
  144. printf("Error polling perf buffer: %d\n", err);
  145. cleanup:
  146. perf_buffer__free(pb);
  147. runqslower_bpf__destroy(obj);
  148. return err != 0;
  149. }