854 lines · 21.5 KB · C
RawDownload
/* SPDX-License-Identifier: GPL-2.0 */#pragma once #ifndef RTLA_ALLOW_CLI_P_H#error "Private header file included outside of cli.c module"#endif #include <errno.h>#include <limits.h>#include <linux/kernel.h>#include <subcmd/parse-options.h> #include "cli.h"#include "osnoise.h"#include "timerlat.h" struct osnoise_cb_data {	struct osnoise_params *params;	char *trace_output;}; struct timerlat_cb_data {	struct timerlat_params *params;	char *trace_output;}; /* * Non-zero default values for parameters */static const int default_dma_latency = -1; /* -1 = unset */static const int default_deepest_idle_state = -2; /* -1 = disable all, -2 = unset */static const int default_output_divisor = 1000;static const int default_bucket_size = 1;static const int default_entries = 256;static const enum stack_format default_stack_format = STACK_FORMAT_TRUNCATE; /* * Range checking for long long and int option callbacks. * * Pass a pointer to a const struct as opt->data to enable range checking. * If opt->data is NULL, no range check is performed. */struct llong_range {	long long min;	long long max;}; struct int_range {	int min;	int max;}; #define LLONG_RANGE(lo, hi) \	(&(const struct llong_range){ .min = (lo), .max = (hi) }) #define INT_RANGE(lo, hi) \	(&(const struct int_range){ .min = (lo), .max = (hi) }) static int check_llong_range(const struct option *opt, long long value){	const struct llong_range *range = opt->data; 	if (!range)		return 0;	if (value < range->min || value > range->max) {		fprintf(stderr, " Error: --%s value %lld is out of range [%lld, %lld]\n",			opt->long_name, value, range->min, range->max);		return -1;	}	return 0;} static int check_int_range(const struct option *opt, int value){	const struct int_range *range = opt->data; 	if (!range)		return 0;	if (value < range->min || value > range->max) {		fprintf(stderr, " Error: --%s value %d is out of range [%d, %d]\n",			opt->long_name, value, range->min, range->max);		return -1;	}	return 0;} /* * OPT_CALLBACK variant that populates .data (for range checking). */#define RTLA_OPT_CALLBACK_DATA(s, l, v, a, h, f, d) \	{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \	  .value = (v), .argh = (a), .help = (h), .callback = (f), \	  .data = (void *)(d) } #define RTLA_OPT_CALLBACK_DATA_DEFVAL(s, l, v, a, h, f, d, dv) \	{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \	  .value = (v), .argh = (a), .help = (h), .callback = (f), \	  .data = (void *)(d), .defval = (intptr_t)(dv) } /* * Shorthand macros for integer/long long command line options using * opt_int_callback/opt_llong_callback, with variants that set defval * and/or data (for range checking). * * Note: defval's type is intptr_t. opt_int_callback interprets it directly as * an int, opt_llong_callback interprets it as a pointer to a long long, as * long long does not fit into intptr_t on 32-bit architectures. */#define RTLA_OPT_LLONG(s, l, v, a, h) \	OPT_CALLBACK(s, l, v, a, h, opt_llong_callback) #define RTLA_OPT_LLONG_DEFVAL(s, l, v, a, h, d) { .type = OPTION_CALLBACK, \	.short_name = (s), .long_name = (l), .value = (v), .argh = (a), \	.help = (h), .callback = opt_llong_callback, .defval = (intptr_t)(d) } #define RTLA_OPT_LLONG_DATA(s, l, v, a, h, d) { .type = OPTION_CALLBACK, \	.short_name = (s), .long_name = (l), .value = (v), .argh = (a), \	.help = (h), .callback = opt_llong_callback, .data = (void *)(d) } #define RTLA_OPT_INT(s, l, v, a, h) \	OPT_CALLBACK(s, l, v, a, h, opt_int_callback) #define RTLA_OPT_INT_DEFVAL(s, l, v, a, h, d) { .type = OPTION_CALLBACK, \	.short_name = (s), .long_name = (l), .value = (v), .argh = (a), \	.help = (h), .callback = opt_int_callback, .defval = (intptr_t)(d) } #define RTLA_OPT_INT_DATA_DEFVAL(s, l, v, a, h, d, dv) { .type = OPTION_CALLBACK, \	.short_name = (s), .long_name = (l), .value = (v), .argh = (a), \	.help = (h), .callback = opt_int_callback, \	.data = (void *)(d), .defval = (intptr_t)(dv) } /* * Macros for command line options common to all tools * * Note: Some of the options are common to both timerlat and osnoise, but * have a slightly different meaning. Such options take additional arguments * that have to be provided by the *_parse_args() function of the corresponding * tool. * * All macros defined here assume the presence of a params variable of * the corresponding tool type (i.e struct timerlat_params or struct osnoise_params) * and a cb_data variable of the matching type. */  #define RTLA_OPT_STOP(short, long, name) OPT_CALLBACK_FLAG(short, long, \	&params->common.stop_us, \	"us", \	"stop trace if " name " is higher than the argument in us", \	opt_llong_callback, PARSE_OPT_NOAUTONEG) #define RTLA_OPT_STOP_TOTAL(short, long, name) OPT_CALLBACK_FLAG(short, long, \	&params->common.stop_total_us, \	"us", \	"stop trace if " name " is higher than the argument in us", \	opt_llong_callback, PARSE_OPT_NOAUTONEG) #define RTLA_OPT_TRACE_OUTPUT(tracer, cb) OPT_CALLBACK_OPTARG('t', "trace", \	(const char **)&cb_data.trace_output, \	tracer "_trace.txt", \	"[file]", \	"save the stopped trace to [file|" tracer "_trace.txt]", \	cb) #define RTLA_OPT_CPUS OPT_CALLBACK('c', "cpus", &params->common, \	"cpu-list", \	"run the tracer only on the given cpus", \	opt_cpus_cb) #define RTLA_OPT_CGROUP OPT_CALLBACK_OPTARG('C', "cgroup", &params->common, \	"[cgroup_name]", NULL, \	"set cgroup, no argument means rtla's cgroup will be inherited", \	opt_cgroup_cb) #define RTLA_OPT_USER_THREADS OPT_CALLBACK_NOOPT('u', "user-threads", params, NULL, \	"use rtla user-space threads instead of kernel-space timerlat threads", \	opt_user_threads_cb) #define RTLA_OPT_KERNEL_THREADS OPT_BOOLEAN('k', "kernel-threads", \	&params->common.kernel_workload, \	"use timerlat kernel-space threads instead of rtla user-space threads") #define RTLA_OPT_USER_LOAD OPT_BOOLEAN('U', "user-load", &params->common.user_data, \	"enable timerlat for user-defined user-space workload") #define RTLA_OPT_DURATION OPT_CALLBACK('d', "duration", &params->common, \	"time[s|m|h|d]", \	"set the duration of the session", \	opt_duration_cb) #define RTLA_OPT_EVENT OPT_CALLBACK('e', "event", &params->common.events, \	"sys:event", \	"enable the <sys:event> in the trace instance, multiple -e are allowed", \	opt_event_cb) #define RTLA_OPT_HOUSEKEEPING OPT_CALLBACK('H', "house-keeping", &params->common, \	"cpu-list", \	"run rtla control threads only on the given cpus", \	opt_housekeeping_cb) #define RTLA_OPT_PRIORITY OPT_CALLBACK('P', "priority", &params->common, \	"o:prio|r:prio|f:prio|d:runtime:period", \	"set scheduling parameters", \	opt_priority_cb) #define RTLA_OPT_TRIGGER OPT_CALLBACK(0, "trigger", &params->common.events, \	"trigger", \	"enable a trace event trigger to the previous -e event", \	opt_trigger_cb) #define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", &params->common.events, \	"filter", \	"enable a trace event filter to the previous -e event", \	opt_filter_cb) #define RTLA_OPT_QUIET OPT_BOOLEAN('q', "quiet", &params->common.quiet, \	"print only a summary at the end") #define RTLA_OPT_TRACE_BUFFER_SIZE RTLA_OPT_INT(0, "trace-buffer-size", \	&params->common.buffer_size, "kB", \	"set the per-cpu trace buffer size in kB") #define RTLA_OPT_WARM_UP RTLA_OPT_INT(0, "warm-up", &params->common.warmup, "s", \	"let the workload run for s seconds before collecting data") #define RTLA_OPT_AUTO(cb) OPT_CALLBACK('a', "auto", &cb_data, "us", \	"set automatic trace mode, stopping the session if argument in us sample is hit", \	cb) #define RTLA_OPT_ON_THRESHOLD(threshold, cb) OPT_CALLBACK(0, "on-threshold", \	&params->common.threshold_actions, \	"action", \	"define action to be executed at " threshold " threshold, multiple are allowed", \	cb) #define RTLA_OPT_ON_END(cb) OPT_CALLBACK(0, "on-end", &params->common.end_actions, \	"action", \	"define action to be executed at measurement end, multiple are allowed", \	cb) #define RTLA_OPT_DEBUG OPT_BOOLEAN('D', "debug", &config_debug, \	"print debug info") /* * Helper functions for parsing numeric option arguments. */static void opt_err(const struct option *opt, const char *arg, const char *msg){	fprintf(stderr, " Error: --%s: '%s' %s\n", opt->long_name, arg, msg);} static int strtoll_safe(const struct option *opt, const char *arg, long long *value){	long long tmp;	char *end; 	errno = 0;	tmp = strtoll(arg, &end, 10);	if (errno || *end || end == arg) {		opt_err(opt, arg, "is not a valid number");		return -1;	}	*value = tmp;	return 0;} static int strtoi_safe(const struct option *opt, const char *arg, int *value){	int tmp; 	if (strtoi(arg, &tmp)) {		opt_err(opt, arg, "is not a valid number");		return -1;	}	*value = tmp;	return 0;} /* * Common callback functions for command line options */ static int opt_llong_callback(const struct option *opt, const char *arg, int unset){	long long *value = opt->value; 	if (unset) {		*value = opt->defval ? *(long long *)opt->defval : 0;		return 0;	} 	if (!arg)		return -1; 	if (strtoll_safe(opt, arg, value))		return -1;	if (check_llong_range(opt, *value))		return -1;	return 0;} static int opt_int_callback(const struct option *opt, const char *arg, int unset){	int *value = opt->value; 	if (unset) {		*value = (int)opt->defval;		return 0;	} 	if (!arg)		return -1; 	if (strtoi_safe(opt, arg, value))		return -1;	if (check_int_range(opt, *value))		return -1; 	return 0;} static int opt_cpus_cb(const struct option *opt, const char *arg, int unset){	struct common_params *params = opt->value;	int retval; 	if (unset) {		CPU_ZERO(&params->monitored_cpus);		params->cpus = NULL;		return 0;	} 	if (!arg)		return -1; 	retval = parse_cpu_set((char *)arg, &params->monitored_cpus);	if (retval) {		opt_err(opt, arg, "is not a valid cpu set");		return -1;	}	params->cpus = (char *)arg; 	return 0;} static int opt_cgroup_cb(const struct option *opt, const char *arg, int unset){	struct common_params *params = opt->value; 	if (unset) {		params->cgroup = 0;		params->cgroup_name = NULL;		return 0;	} 	params->cgroup = 1;	params->cgroup_name = (char *)arg;	if (params->cgroup_name && params->cgroup_name[0] == '=')		/* Allow -C=<cgroup_name> next to -C[ ]<cgroup_name> */		++params->cgroup_name; 	return 0;} static int opt_duration_cb(const struct option *opt, const char *arg, int unset){	struct common_params *params = opt->value; 	if (unset) {		params->duration = 0;		return 0;	} 	if (!arg)		return -1; 	params->duration = parse_seconds_duration((char *)arg);	if (!params->duration) {		opt_err(opt, arg, "is not a valid duration");		return -1;	} 	return 0;} static int opt_event_cb(const struct option *opt, const char *arg, int unset){	struct trace_events **events = opt->value;	struct trace_events *tevent; 	if (unset || !arg)		return -1; 	tevent = trace_event_alloc((char *)arg);	if (!tevent)		fatal("Error alloc trace event"); 	if (*events)		tevent->next = *events;	*events = tevent; 	return 0;} static int opt_housekeeping_cb(const struct option *opt, const char *arg, int unset){	struct common_params *params = opt->value;	int retval; 	if (unset) {		params->hk_cpus = 0;		CPU_ZERO(&params->hk_cpu_set);		return 0;	} 	if (!arg)		return -1; 	params->hk_cpus = 1;	retval = parse_cpu_set((char *)arg, &params->hk_cpu_set);	if (retval) {		opt_err(opt, arg, "is not a valid cpu set");		return -1;	} 	return 0;} static int opt_priority_cb(const struct option *opt, const char *arg, int unset){	struct common_params *params = opt->value;	int retval; 	if (unset) {		memset(&params->sched_param, 0, sizeof(params->sched_param));		params->set_sched = 0;		return 0;	} 	if (!arg)		return -1; 	retval = parse_prio((char *)arg, &params->sched_param);	if (retval == -1) {		opt_err(opt, arg, "is not a valid priority");		return -1;	}	params->set_sched = 1; 	return 0;} static int opt_trigger_cb(const struct option *opt, const char *arg, int unset){	struct trace_events **events = opt->value; 	if (unset || !arg)		return -1; 	if (!*events) {		opt_err(opt, arg, "has no previous event to apply to");		return -1;	} 	trace_event_add_trigger(*events, (char *)arg); 	return 0;} static int opt_filter_cb(const struct option *opt, const char *arg, int unset){	struct trace_events **events = opt->value; 	if (unset || !arg)		return -1; 	if (!*events) {		opt_err(opt, arg, "has no previous event to apply to");		return -1;	} 	trace_event_add_filter(*events, (char *)arg); 	return 0;} /* * Macros for command line options specific to osnoise */#define OSNOISE_OPT_PERIOD RTLA_OPT_LLONG_DATA('p', "period", &params->period, "us", \	"osnoise period in us", \	LLONG_RANGE(1, 10000000)) #define OSNOISE_OPT_RUNTIME RTLA_OPT_LLONG_DATA('r', "runtime", &params->runtime, "us", \	"osnoise runtime in us", \	LLONG_RANGE(100, LLONG_MAX)) #define OSNOISE_OPT_THRESHOLD RTLA_OPT_LLONG('T', "threshold", &params->threshold, "us", \	"the minimum delta to be considered a noise") /*

Lines 1–500 of 854.