18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
28c2ecf20Sopenharmony_ci// Copyright (c) 2019 Facebook
38c2ecf20Sopenharmony_ci#include <argp.h>
48c2ecf20Sopenharmony_ci#include <stdio.h>
58c2ecf20Sopenharmony_ci#include <stdlib.h>
68c2ecf20Sopenharmony_ci#include <string.h>
78c2ecf20Sopenharmony_ci#include <sys/resource.h>
88c2ecf20Sopenharmony_ci#include <time.h>
98c2ecf20Sopenharmony_ci#include <bpf/libbpf.h>
108c2ecf20Sopenharmony_ci#include <bpf/bpf.h>
118c2ecf20Sopenharmony_ci#include "runqslower.h"
128c2ecf20Sopenharmony_ci#include "runqslower.skel.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct env {
158c2ecf20Sopenharmony_ci	pid_t pid;
168c2ecf20Sopenharmony_ci	__u64 min_us;
178c2ecf20Sopenharmony_ci	bool verbose;
188c2ecf20Sopenharmony_ci} env = {
198c2ecf20Sopenharmony_ci	.min_us = 10000,
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciconst char *argp_program_version = "runqslower 0.1";
238c2ecf20Sopenharmony_ciconst char *argp_program_bug_address = "<bpf@vger.kernel.org>";
248c2ecf20Sopenharmony_ciconst char argp_program_doc[] =
258c2ecf20Sopenharmony_ci"runqslower    Trace long process scheduling delays.\n"
268c2ecf20Sopenharmony_ci"              For Linux, uses eBPF, BPF CO-RE, libbpf, BTF.\n"
278c2ecf20Sopenharmony_ci"\n"
288c2ecf20Sopenharmony_ci"This script traces high scheduling delays between tasks being\n"
298c2ecf20Sopenharmony_ci"ready to run and them running on CPU after that.\n"
308c2ecf20Sopenharmony_ci"\n"
318c2ecf20Sopenharmony_ci"USAGE: runqslower [-p PID] [min_us]\n"
328c2ecf20Sopenharmony_ci"\n"
338c2ecf20Sopenharmony_ci"EXAMPLES:\n"
348c2ecf20Sopenharmony_ci"    runqslower         # trace run queue latency higher than 10000 us (default)\n"
358c2ecf20Sopenharmony_ci"    runqslower 1000    # trace run queue latency higher than 1000 us\n"
368c2ecf20Sopenharmony_ci"    runqslower -p 123  # trace pid 123 only\n";
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const struct argp_option opts[] = {
398c2ecf20Sopenharmony_ci	{ "pid", 'p', "PID", 0, "Process PID to trace"},
408c2ecf20Sopenharmony_ci	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
418c2ecf20Sopenharmony_ci	{},
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic error_t parse_arg(int key, char *arg, struct argp_state *state)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	static int pos_args;
478c2ecf20Sopenharmony_ci	int pid;
488c2ecf20Sopenharmony_ci	long long min_us;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	switch (key) {
518c2ecf20Sopenharmony_ci	case 'v':
528c2ecf20Sopenharmony_ci		env.verbose = true;
538c2ecf20Sopenharmony_ci		break;
548c2ecf20Sopenharmony_ci	case 'p':
558c2ecf20Sopenharmony_ci		errno = 0;
568c2ecf20Sopenharmony_ci		pid = strtol(arg, NULL, 10);
578c2ecf20Sopenharmony_ci		if (errno || pid <= 0) {
588c2ecf20Sopenharmony_ci			fprintf(stderr, "Invalid PID: %s\n", arg);
598c2ecf20Sopenharmony_ci			argp_usage(state);
608c2ecf20Sopenharmony_ci		}
618c2ecf20Sopenharmony_ci		env.pid = pid;
628c2ecf20Sopenharmony_ci		break;
638c2ecf20Sopenharmony_ci	case ARGP_KEY_ARG:
648c2ecf20Sopenharmony_ci		if (pos_args++) {
658c2ecf20Sopenharmony_ci			fprintf(stderr,
668c2ecf20Sopenharmony_ci				"Unrecognized positional argument: %s\n", arg);
678c2ecf20Sopenharmony_ci			argp_usage(state);
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci		errno = 0;
708c2ecf20Sopenharmony_ci		min_us = strtoll(arg, NULL, 10);
718c2ecf20Sopenharmony_ci		if (errno || min_us <= 0) {
728c2ecf20Sopenharmony_ci			fprintf(stderr, "Invalid delay (in us): %s\n", arg);
738c2ecf20Sopenharmony_ci			argp_usage(state);
748c2ecf20Sopenharmony_ci		}
758c2ecf20Sopenharmony_ci		env.min_us = min_us;
768c2ecf20Sopenharmony_ci		break;
778c2ecf20Sopenharmony_ci	default:
788c2ecf20Sopenharmony_ci		return ARGP_ERR_UNKNOWN;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ciint libbpf_print_fn(enum libbpf_print_level level,
848c2ecf20Sopenharmony_ci		    const char *format, va_list args)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	if (level == LIBBPF_DEBUG && !env.verbose)
878c2ecf20Sopenharmony_ci		return 0;
888c2ecf20Sopenharmony_ci	return vfprintf(stderr, format, args);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int bump_memlock_rlimit(void)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct rlimit rlim_new = {
948c2ecf20Sopenharmony_ci		.rlim_cur	= RLIM_INFINITY,
958c2ecf20Sopenharmony_ci		.rlim_max	= RLIM_INFINITY,
968c2ecf20Sopenharmony_ci	};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_civoid handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	const struct event *e = data;
1048c2ecf20Sopenharmony_ci	struct tm *tm;
1058c2ecf20Sopenharmony_ci	char ts[32];
1068c2ecf20Sopenharmony_ci	time_t t;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	time(&t);
1098c2ecf20Sopenharmony_ci	tm = localtime(&t);
1108c2ecf20Sopenharmony_ci	strftime(ts, sizeof(ts), "%H:%M:%S", tm);
1118c2ecf20Sopenharmony_ci	printf("%-8s %-16s %-6d %14llu\n", ts, e->task, e->pid, e->delta_us);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_civoid handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	printf("Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ciint main(int argc, char **argv)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	static const struct argp argp = {
1228c2ecf20Sopenharmony_ci		.options = opts,
1238c2ecf20Sopenharmony_ci		.parser = parse_arg,
1248c2ecf20Sopenharmony_ci		.doc = argp_program_doc,
1258c2ecf20Sopenharmony_ci	};
1268c2ecf20Sopenharmony_ci	struct perf_buffer_opts pb_opts;
1278c2ecf20Sopenharmony_ci	struct perf_buffer *pb = NULL;
1288c2ecf20Sopenharmony_ci	struct runqslower_bpf *obj;
1298c2ecf20Sopenharmony_ci	int err;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
1328c2ecf20Sopenharmony_ci	if (err)
1338c2ecf20Sopenharmony_ci		return err;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	libbpf_set_print(libbpf_print_fn);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	err = bump_memlock_rlimit();
1388c2ecf20Sopenharmony_ci	if (err) {
1398c2ecf20Sopenharmony_ci		fprintf(stderr, "failed to increase rlimit: %d", err);
1408c2ecf20Sopenharmony_ci		return 1;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	obj = runqslower_bpf__open();
1448c2ecf20Sopenharmony_ci	if (!obj) {
1458c2ecf20Sopenharmony_ci		fprintf(stderr, "failed to open and/or load BPF object\n");
1468c2ecf20Sopenharmony_ci		return 1;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* initialize global data (filtering options) */
1508c2ecf20Sopenharmony_ci	obj->rodata->targ_pid = env.pid;
1518c2ecf20Sopenharmony_ci	obj->rodata->min_us = env.min_us;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	err = runqslower_bpf__load(obj);
1548c2ecf20Sopenharmony_ci	if (err) {
1558c2ecf20Sopenharmony_ci		fprintf(stderr, "failed to load BPF object: %d\n", err);
1568c2ecf20Sopenharmony_ci		goto cleanup;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	err = runqslower_bpf__attach(obj);
1608c2ecf20Sopenharmony_ci	if (err) {
1618c2ecf20Sopenharmony_ci		fprintf(stderr, "failed to attach BPF programs\n");
1628c2ecf20Sopenharmony_ci		goto cleanup;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	printf("Tracing run queue latency higher than %llu us\n", env.min_us);
1668c2ecf20Sopenharmony_ci	printf("%-8s %-16s %-6s %14s\n", "TIME", "COMM", "PID", "LAT(us)");
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	pb_opts.sample_cb = handle_event;
1698c2ecf20Sopenharmony_ci	pb_opts.lost_cb = handle_lost_events;
1708c2ecf20Sopenharmony_ci	pb = perf_buffer__new(bpf_map__fd(obj->maps.events), 64, &pb_opts);
1718c2ecf20Sopenharmony_ci	err = libbpf_get_error(pb);
1728c2ecf20Sopenharmony_ci	if (err) {
1738c2ecf20Sopenharmony_ci		pb = NULL;
1748c2ecf20Sopenharmony_ci		fprintf(stderr, "failed to open perf buffer: %d\n", err);
1758c2ecf20Sopenharmony_ci		goto cleanup;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	while ((err = perf_buffer__poll(pb, 100)) >= 0)
1798c2ecf20Sopenharmony_ci		;
1808c2ecf20Sopenharmony_ci	printf("Error polling perf buffer: %d\n", err);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cicleanup:
1838c2ecf20Sopenharmony_ci	perf_buffer__free(pb);
1848c2ecf20Sopenharmony_ci	runqslower_bpf__destroy(obj);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return err != 0;
1878c2ecf20Sopenharmony_ci}
188