brintos

brintos / linux-shallow public Read only

0
0
Text · 29.6 KiB · 210b0f5 Raw
1140 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>4 */5 6#define _GNU_SOURCE7#include <getopt.h>8#include <stdlib.h>9#include <string.h>10#include <signal.h>11#include <unistd.h>12#include <stdio.h>13#include <time.h>14#include <errno.h>15#include <sched.h>16#include <pthread.h>17 18#include "utils.h"19#include "osnoise.h"20#include "timerlat.h"21#include "timerlat_aa.h"22#include "timerlat_u.h"23 24struct timerlat_top_params {25	char			*cpus;26	cpu_set_t		monitored_cpus;27	char			*trace_output;28	char			*cgroup_name;29	unsigned long long	runtime;30	long long		stop_us;31	long long		stop_total_us;32	long long		timerlat_period_us;33	long long		print_stack;34	int			sleep_time;35	int			output_divisor;36	int			duration;37	int			quiet;38	int			set_sched;39	int			dma_latency;40	int			no_aa;41	int			aa_only;42	int			dump_tasks;43	int			cgroup;44	int			hk_cpus;45	int			user_top;46	int			user_workload;47	int			kernel_workload;48	int			pretty_output;49	int			warmup;50	int			buffer_size;51	cpu_set_t		hk_cpu_set;52	struct sched_attr	sched_param;53	struct trace_events	*events;54};55 56struct timerlat_top_cpu {57	int			irq_count;58	int			thread_count;59	int			user_count;60 61	unsigned long long	cur_irq;62	unsigned long long	min_irq;63	unsigned long long	sum_irq;64	unsigned long long	max_irq;65 66	unsigned long long	cur_thread;67	unsigned long long	min_thread;68	unsigned long long	sum_thread;69	unsigned long long	max_thread;70 71	unsigned long long	cur_user;72	unsigned long long	min_user;73	unsigned long long	sum_user;74	unsigned long long	max_user;75};76 77struct timerlat_top_data {78	struct timerlat_top_cpu	*cpu_data;79	int			nr_cpus;80};81 82/*83 * timerlat_free_top - free runtime data84 */85static void86timerlat_free_top(struct timerlat_top_data *data)87{88	free(data->cpu_data);89	free(data);90}91 92/*93 * timerlat_alloc_histogram - alloc runtime data94 */95static struct timerlat_top_data *timerlat_alloc_top(int nr_cpus)96{97	struct timerlat_top_data *data;98	int cpu;99 100	data = calloc(1, sizeof(*data));101	if (!data)102		return NULL;103 104	data->nr_cpus = nr_cpus;105 106	/* one set of histograms per CPU */107	data->cpu_data = calloc(1, sizeof(*data->cpu_data) * nr_cpus);108	if (!data->cpu_data)109		goto cleanup;110 111	/* set the min to max */112	for (cpu = 0; cpu < nr_cpus; cpu++) {113		data->cpu_data[cpu].min_irq = ~0;114		data->cpu_data[cpu].min_thread = ~0;115		data->cpu_data[cpu].min_user = ~0;116	}117 118	return data;119 120cleanup:121	timerlat_free_top(data);122	return NULL;123}124 125static void126timerlat_top_reset_sum(struct timerlat_top_cpu *summary)127{128	memset(summary, 0, sizeof(*summary));129	summary->min_irq = ~0;130	summary->min_thread = ~0;131	summary->min_user = ~0;132}133 134static void135timerlat_top_update_sum(struct osnoise_tool *tool, int cpu, struct timerlat_top_cpu *sum)136{137	struct timerlat_top_data *data = tool->data;138	struct timerlat_top_cpu *cpu_data = &data->cpu_data[cpu];139 140	sum->irq_count += cpu_data->irq_count;141	update_min(&sum->min_irq, &cpu_data->min_irq);142	update_sum(&sum->sum_irq, &cpu_data->sum_irq);143	update_max(&sum->max_irq, &cpu_data->max_irq);144 145	sum->thread_count += cpu_data->thread_count;146	update_min(&sum->min_thread, &cpu_data->min_thread);147	update_sum(&sum->sum_thread, &cpu_data->sum_thread);148	update_max(&sum->max_thread, &cpu_data->max_thread);149 150	sum->user_count += cpu_data->user_count;151	update_min(&sum->min_user, &cpu_data->min_user);152	update_sum(&sum->sum_user, &cpu_data->sum_user);153	update_max(&sum->max_user, &cpu_data->max_user);154}155 156/*157 * timerlat_hist_update - record a new timerlat occurent on cpu, updating data158 */159static void160timerlat_top_update(struct osnoise_tool *tool, int cpu,161		    unsigned long long thread,162		    unsigned long long latency)163{164	struct timerlat_top_data *data = tool->data;165	struct timerlat_top_cpu *cpu_data = &data->cpu_data[cpu];166 167	if (!thread) {168		cpu_data->irq_count++;169		cpu_data->cur_irq = latency;170		update_min(&cpu_data->min_irq, &latency);171		update_sum(&cpu_data->sum_irq, &latency);172		update_max(&cpu_data->max_irq, &latency);173	} else if (thread == 1) {174		cpu_data->thread_count++;175		cpu_data->cur_thread = latency;176		update_min(&cpu_data->min_thread, &latency);177		update_sum(&cpu_data->sum_thread, &latency);178		update_max(&cpu_data->max_thread, &latency);179	} else {180		cpu_data->user_count++;181		cpu_data->cur_user = latency;182		update_min(&cpu_data->min_user, &latency);183		update_sum(&cpu_data->sum_user, &latency);184		update_max(&cpu_data->max_user, &latency);185	}186}187 188/*189 * timerlat_top_handler - this is the handler for timerlat tracer events190 */191static int192timerlat_top_handler(struct trace_seq *s, struct tep_record *record,193		     struct tep_event *event, void *context)194{195	struct trace_instance *trace = context;196	struct timerlat_top_params *params;197	unsigned long long latency, thread;198	struct osnoise_tool *top;199	int cpu = record->cpu;200 201	top = container_of(trace, struct osnoise_tool, trace);202	params = top->params;203 204	if (!params->aa_only) {205		tep_get_field_val(s, event, "context", record, &thread, 1);206		tep_get_field_val(s, event, "timer_latency", record, &latency, 1);207 208		timerlat_top_update(top, cpu, thread, latency);209	}210 211	return 0;212}213 214/*215 * timerlat_top_header - print the header of the tool output216 */217static void timerlat_top_header(struct timerlat_top_params *params, struct osnoise_tool *top)218{219	struct trace_seq *s = top->trace.seq;220	char duration[26];221 222	get_duration(top->start_time, duration, sizeof(duration));223 224	if (params->pretty_output)225		trace_seq_printf(s, "\033[2;37;40m");226 227	trace_seq_printf(s, "                                     Timer Latency                                              ");228	if (params->user_top)229		trace_seq_printf(s, "                                         ");230 231	if (params->pretty_output)232		trace_seq_printf(s, "\033[0;0;0m");233	trace_seq_printf(s, "\n");234 235	trace_seq_printf(s, "%-6s   |          IRQ Timer Latency (%s)        |         Thread Timer Latency (%s)", duration,236			params->output_divisor == 1 ? "ns" : "us",237			params->output_divisor == 1 ? "ns" : "us");238 239	if (params->user_top) {240		trace_seq_printf(s, "      |    Ret user Timer Latency (%s)",241				params->output_divisor == 1 ? "ns" : "us");242	}243 244	trace_seq_printf(s, "\n");245	if (params->pretty_output)246		trace_seq_printf(s, "\033[2;30;47m");247 248	trace_seq_printf(s, "CPU COUNT      |      cur       min       avg       max |      cur       min       avg       max");249	if (params->user_top)250		trace_seq_printf(s, " |      cur       min       avg       max");251 252	if (params->pretty_output)253		trace_seq_printf(s, "\033[0;0;0m");254	trace_seq_printf(s, "\n");255}256 257static const char *no_value = "        -";258 259/*260 * timerlat_top_print - prints the output of a given CPU261 */262static void timerlat_top_print(struct osnoise_tool *top, int cpu)263{264 265	struct timerlat_top_params *params = top->params;266	struct timerlat_top_data *data = top->data;267	struct timerlat_top_cpu *cpu_data = &data->cpu_data[cpu];268	int divisor = params->output_divisor;269	struct trace_seq *s = top->trace.seq;270 271	if (divisor == 0)272		return;273 274	/*275	 * Skip if no data is available: is this cpu offline?276	 */277	if (!cpu_data->irq_count && !cpu_data->thread_count)278		return;279 280	/*281	 * Unless trace is being lost, IRQ counter is always the max.282	 */283	trace_seq_printf(s, "%3d #%-9d |", cpu, cpu_data->irq_count);284 285	if (!cpu_data->irq_count) {286		trace_seq_printf(s, "%s %s %s %s |", no_value, no_value, no_value, no_value);287	} else {288		trace_seq_printf(s, "%9llu ", cpu_data->cur_irq / params->output_divisor);289		trace_seq_printf(s, "%9llu ", cpu_data->min_irq / params->output_divisor);290		trace_seq_printf(s, "%9llu ", (cpu_data->sum_irq / cpu_data->irq_count) / divisor);291		trace_seq_printf(s, "%9llu |", cpu_data->max_irq / divisor);292	}293 294	if (!cpu_data->thread_count) {295		trace_seq_printf(s, "%s %s %s %s", no_value, no_value, no_value, no_value);296	} else {297		trace_seq_printf(s, "%9llu ", cpu_data->cur_thread / divisor);298		trace_seq_printf(s, "%9llu ", cpu_data->min_thread / divisor);299		trace_seq_printf(s, "%9llu ",300				(cpu_data->sum_thread / cpu_data->thread_count) / divisor);301		trace_seq_printf(s, "%9llu", cpu_data->max_thread / divisor);302	}303 304	if (!params->user_top) {305		trace_seq_printf(s, "\n");306		return;307	}308 309	trace_seq_printf(s, " |");310 311	if (!cpu_data->user_count) {312		trace_seq_printf(s, "%s %s %s %s\n", no_value, no_value, no_value, no_value);313	} else {314		trace_seq_printf(s, "%9llu ", cpu_data->cur_user / divisor);315		trace_seq_printf(s, "%9llu ", cpu_data->min_user / divisor);316		trace_seq_printf(s, "%9llu ",317				(cpu_data->sum_user / cpu_data->user_count) / divisor);318		trace_seq_printf(s, "%9llu\n", cpu_data->max_user / divisor);319	}320}321 322/*323 * timerlat_top_print_sum - prints the summary output324 */325static void326timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summary)327{328	const char *split = "----------------------------------------";329	struct timerlat_top_params *params = top->params;330	unsigned long long count = summary->irq_count;331	int divisor = params->output_divisor;332	struct trace_seq *s = top->trace.seq;333	int e = 0;334 335	if (divisor == 0)336		return;337 338	/*339	 * Skip if no data is available: is this cpu offline?340	 */341	if (!summary->irq_count && !summary->thread_count)342		return;343 344	while (count > 999999) {345		e++;346		count /= 10;347	}348 349	trace_seq_printf(s, "%.*s|%.*s|%.*s", 15, split, 40, split, 39, split);350	if (params->user_top)351		trace_seq_printf(s, "-|%.*s", 39, split);352	trace_seq_printf(s, "\n");353 354	trace_seq_printf(s, "ALL #%-6llu e%d |", count, e);355 356	if (!summary->irq_count) {357		trace_seq_printf(s, "          %s %s %s |", no_value, no_value, no_value);358	} else {359		trace_seq_printf(s, "          ");360		trace_seq_printf(s, "%9llu ", summary->min_irq / params->output_divisor);361		trace_seq_printf(s, "%9llu ", (summary->sum_irq / summary->irq_count) / divisor);362		trace_seq_printf(s, "%9llu |", summary->max_irq / divisor);363	}364 365	if (!summary->thread_count) {366		trace_seq_printf(s, "%s %s %s %s", no_value, no_value, no_value, no_value);367	} else {368		trace_seq_printf(s, "          ");369		trace_seq_printf(s, "%9llu ", summary->min_thread / divisor);370		trace_seq_printf(s, "%9llu ",371				(summary->sum_thread / summary->thread_count) / divisor);372		trace_seq_printf(s, "%9llu", summary->max_thread / divisor);373	}374 375	if (!params->user_top) {376		trace_seq_printf(s, "\n");377		return;378	}379 380	trace_seq_printf(s, " |");381 382	if (!summary->user_count) {383		trace_seq_printf(s, "          %s %s %s |", no_value, no_value, no_value);384	} else {385		trace_seq_printf(s, "          ");386		trace_seq_printf(s, "%9llu ", summary->min_user / divisor);387		trace_seq_printf(s, "%9llu ",388				(summary->sum_user / summary->user_count) / divisor);389		trace_seq_printf(s, "%9llu\n", summary->max_user / divisor);390	}391}392 393/*394 * clear_terminal - clears the output terminal395 */396static void clear_terminal(struct trace_seq *seq)397{398	if (!config_debug)399		trace_seq_printf(seq, "\033c");400}401 402/*403 * timerlat_print_stats - print data for all cpus404 */405static void406timerlat_print_stats(struct timerlat_top_params *params, struct osnoise_tool *top)407{408	struct trace_instance *trace = &top->trace;409	struct timerlat_top_cpu summary;410	static int nr_cpus = -1;411	int i;412 413	if (params->aa_only)414		return;415 416	if (nr_cpus == -1)417		nr_cpus = sysconf(_SC_NPROCESSORS_CONF);418 419	if (!params->quiet)420		clear_terminal(trace->seq);421 422	timerlat_top_reset_sum(&summary);423 424	timerlat_top_header(params, top);425 426	for (i = 0; i < nr_cpus; i++) {427		if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))428			continue;429		timerlat_top_print(top, i);430		timerlat_top_update_sum(top, i, &summary);431	}432 433	timerlat_top_print_sum(top, &summary);434 435	trace_seq_do_printf(trace->seq);436	trace_seq_reset(trace->seq);437}438 439/*440 * timerlat_top_usage - prints timerlat top usage message441 */442static void timerlat_top_usage(char *usage)443{444	int i;445 446	static const char *const msg[] = {447		"",448		"  usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\",449		"	  [[-t[file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",450		"	  [-P priority] [--dma-latency us] [--aa-only us] [-C[=cgroup_name]] [-u|-k] [--warm-up s]",451		"",452		"	  -h/--help: print this menu",453		"	  -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",454		"	     --aa-only us: stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)",455		"	  -p/--period us: timerlat period in us",456		"	  -i/--irq us: stop trace if the irq latency is higher than the argument in us",457		"	  -T/--thread us: stop trace if the thread latency is higher than the argument in us",458		"	  -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us",459		"	  -c/--cpus cpus: run the tracer only on the given cpus",460		"	  -H/--house-keeping cpus: run rtla control threads only on the given cpus",461		"	  -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",462		"	  -d/--duration time[s|m|h|d]: duration of the session",463		"	  -D/--debug: print debug info",464		"	     --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)",465		"	  -t/--trace[file]: save the stopped trace to [file|timerlat_trace.txt]",466		"	  -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",467		"	     --filter <command>: enable a trace event filter to the previous -e event",468		"	     --trigger <command>: enable a trace event trigger to the previous -e event",469		"	  -n/--nano: display data in nanoseconds",470		"	     --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage",471		"	  -q/--quiet print only a summary at the end",472		"	     --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",473		"	  -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",474		"		o:prio - use SCHED_OTHER with prio",475		"		r:prio - use SCHED_RR with prio",476		"		f:prio - use SCHED_FIFO with prio",477		"		d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",478		"						       in nanoseconds",479		"	  -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads",480		"	  -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads",481		"	  -U/--user-load: enable timerlat for user-defined user-space workload",482		"	     --warm-up s: let the workload run for s seconds before collecting data",483		"	     --trace-buffer-size kB: set the per-cpu trace buffer size in kB",484		NULL,485	};486 487	if (usage)488		fprintf(stderr, "%s\n", usage);489 490	fprintf(stderr, "rtla timerlat top: a per-cpu summary of the timer latency (version %s)\n",491			VERSION);492 493	for (i = 0; msg[i]; i++)494		fprintf(stderr, "%s\n", msg[i]);495 496	if (usage)497		exit(EXIT_FAILURE);498 499	exit(EXIT_SUCCESS);500}501 502/*503 * timerlat_top_parse_args - allocs, parse and fill the cmd line parameters504 */505static struct timerlat_top_params506*timerlat_top_parse_args(int argc, char **argv)507{508	struct timerlat_top_params *params;509	struct trace_events *tevent;510	long long auto_thresh;511	int retval;512	int c;513 514	params = calloc(1, sizeof(*params));515	if (!params)516		exit(1);517 518	/* disabled by default */519	params->dma_latency = -1;520 521	/* display data in microseconds */522	params->output_divisor = 1000;523 524	while (1) {525		static struct option long_options[] = {526			{"auto",		required_argument,	0, 'a'},527			{"cpus",		required_argument,	0, 'c'},528			{"cgroup",		optional_argument,	0, 'C'},529			{"debug",		no_argument,		0, 'D'},530			{"duration",		required_argument,	0, 'd'},531			{"event",		required_argument,	0, 'e'},532			{"help",		no_argument,		0, 'h'},533			{"house-keeping",	required_argument,	0, 'H'},534			{"irq",			required_argument,	0, 'i'},535			{"nano",		no_argument,		0, 'n'},536			{"period",		required_argument,	0, 'p'},537			{"priority",		required_argument,	0, 'P'},538			{"quiet",		no_argument,		0, 'q'},539			{"stack",		required_argument,	0, 's'},540			{"thread",		required_argument,	0, 'T'},541			{"trace",		optional_argument,	0, 't'},542			{"user-threads",	no_argument,		0, 'u'},543			{"kernel-threads",	no_argument,		0, 'k'},544			{"user-load",		no_argument,		0, 'U'},545			{"trigger",		required_argument,	0, '0'},546			{"filter",		required_argument,	0, '1'},547			{"dma-latency",		required_argument,	0, '2'},548			{"no-aa",		no_argument,		0, '3'},549			{"dump-tasks",		no_argument,		0, '4'},550			{"aa-only",		required_argument,	0, '5'},551			{"warm-up",		required_argument,	0, '6'},552			{"trace-buffer-size",	required_argument,	0, '7'},553			{0, 0, 0, 0}554		};555 556		/* getopt_long stores the option index here. */557		int option_index = 0;558 559		c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:knp:P:qs:t::T:uU0:1:2:345:6:7:",560				 long_options, &option_index);561 562		/* detect the end of the options. */563		if (c == -1)564			break;565 566		switch (c) {567		case 'a':568			auto_thresh = get_llong_from_str(optarg);569 570			/* set thread stop to auto_thresh */571			params->stop_total_us = auto_thresh;572			params->stop_us = auto_thresh;573 574			/* get stack trace */575			params->print_stack = auto_thresh;576 577			/* set trace */578			params->trace_output = "timerlat_trace.txt";579			break;580		case '5':581			/* it is here because it is similar to -a */582			auto_thresh = get_llong_from_str(optarg);583 584			/* set thread stop to auto_thresh */585			params->stop_total_us = auto_thresh;586			params->stop_us = auto_thresh;587 588			/* get stack trace */589			params->print_stack = auto_thresh;590 591			/* set aa_only to avoid parsing the trace */592			params->aa_only = 1;593			break;594		case 'c':595			retval = parse_cpu_set(optarg, &params->monitored_cpus);596			if (retval)597				timerlat_top_usage("\nInvalid -c cpu list\n");598			params->cpus = optarg;599			break;600		case 'C':601			params->cgroup = 1;602			if (!optarg) {603				/* will inherit this cgroup */604				params->cgroup_name = NULL;605			} else if (*optarg == '=') {606				/* skip the = */607				params->cgroup_name = ++optarg;608			}609			break;610		case 'D':611			config_debug = 1;612			break;613		case 'd':614			params->duration = parse_seconds_duration(optarg);615			if (!params->duration)616				timerlat_top_usage("Invalid -d duration\n");617			break;618		case 'e':619			tevent = trace_event_alloc(optarg);620			if (!tevent) {621				err_msg("Error alloc trace event");622				exit(EXIT_FAILURE);623			}624 625			if (params->events)626				tevent->next = params->events;627			params->events = tevent;628			break;629		case 'h':630		case '?':631			timerlat_top_usage(NULL);632			break;633		case 'H':634			params->hk_cpus = 1;635			retval = parse_cpu_set(optarg, &params->hk_cpu_set);636			if (retval) {637				err_msg("Error parsing house keeping CPUs\n");638				exit(EXIT_FAILURE);639			}640			break;641		case 'i':642			params->stop_us = get_llong_from_str(optarg);643			break;644		case 'k':645			params->kernel_workload = true;646			break;647		case 'n':648			params->output_divisor = 1;649			break;650		case 'p':651			params->timerlat_period_us = get_llong_from_str(optarg);652			if (params->timerlat_period_us > 1000000)653				timerlat_top_usage("Period longer than 1 s\n");654			break;655		case 'P':656			retval = parse_prio(optarg, &params->sched_param);657			if (retval == -1)658				timerlat_top_usage("Invalid -P priority");659			params->set_sched = 1;660			break;661		case 'q':662			params->quiet = 1;663			break;664		case 's':665			params->print_stack = get_llong_from_str(optarg);666			break;667		case 'T':668			params->stop_total_us = get_llong_from_str(optarg);669			break;670		case 't':671			if (optarg) {672				if (optarg[0] == '=')673					params->trace_output = &optarg[1];674				else675					params->trace_output = &optarg[0];676			} else if (optind < argc && argv[optind][0] != '-')677				params->trace_output = argv[optind];678			else679				params->trace_output = "timerlat_trace.txt";680 681			break;682		case 'u':683			params->user_workload = true;684			/* fallback: -u implies -U */685		case 'U':686			params->user_top = true;687			break;688		case '0': /* trigger */689			if (params->events) {690				retval = trace_event_add_trigger(params->events, optarg);691				if (retval) {692					err_msg("Error adding trigger %s\n", optarg);693					exit(EXIT_FAILURE);694				}695			} else {696				timerlat_top_usage("--trigger requires a previous -e\n");697			}698			break;699		case '1': /* filter */700			if (params->events) {701				retval = trace_event_add_filter(params->events, optarg);702				if (retval) {703					err_msg("Error adding filter %s\n", optarg);704					exit(EXIT_FAILURE);705				}706			} else {707				timerlat_top_usage("--filter requires a previous -e\n");708			}709			break;710		case '2': /* dma-latency */711			params->dma_latency = get_llong_from_str(optarg);712			if (params->dma_latency < 0 || params->dma_latency > 10000) {713				err_msg("--dma-latency needs to be >= 0 and < 10000");714				exit(EXIT_FAILURE);715			}716			break;717		case '3': /* no-aa */718			params->no_aa = 1;719			break;720		case '4':721			params->dump_tasks = 1;722			break;723		case '6':724			params->warmup = get_llong_from_str(optarg);725			break;726		case '7':727			params->buffer_size = get_llong_from_str(optarg);728			break;729		default:730			timerlat_top_usage("Invalid option");731		}732	}733 734	if (geteuid()) {735		err_msg("rtla needs root permission\n");736		exit(EXIT_FAILURE);737	}738 739	/*740	 * Auto analysis only happens if stop tracing, thus:741	 */742	if (!params->stop_us && !params->stop_total_us)743		params->no_aa = 1;744 745	if (params->no_aa && params->aa_only)746		timerlat_top_usage("--no-aa and --aa-only are mutually exclusive!");747 748	if (params->kernel_workload && params->user_workload)749		timerlat_top_usage("--kernel-threads and --user-threads are mutually exclusive!");750 751	return params;752}753 754/*755 * timerlat_top_apply_config - apply the top configs to the initialized tool756 */757static int758timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_top_params *params)759{760	int retval;761	int i;762 763	if (!params->sleep_time)764		params->sleep_time = 1;765 766	if (params->cpus) {767		retval = osnoise_set_cpus(top->context, params->cpus);768		if (retval) {769			err_msg("Failed to apply CPUs config\n");770			goto out_err;771		}772	} else {773		for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)774			CPU_SET(i, &params->monitored_cpus);775	}776 777	if (params->stop_us) {778		retval = osnoise_set_stop_us(top->context, params->stop_us);779		if (retval) {780			err_msg("Failed to set stop us\n");781			goto out_err;782		}783	}784 785	if (params->stop_total_us) {786		retval = osnoise_set_stop_total_us(top->context, params->stop_total_us);787		if (retval) {788			err_msg("Failed to set stop total us\n");789			goto out_err;790		}791	}792 793 794	if (params->timerlat_period_us) {795		retval = osnoise_set_timerlat_period_us(top->context, params->timerlat_period_us);796		if (retval) {797			err_msg("Failed to set timerlat period\n");798			goto out_err;799		}800	}801 802 803	if (params->print_stack) {804		retval = osnoise_set_print_stack(top->context, params->print_stack);805		if (retval) {806			err_msg("Failed to set print stack\n");807			goto out_err;808		}809	}810 811	if (params->hk_cpus) {812		retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),813					   &params->hk_cpu_set);814		if (retval == -1) {815			err_msg("Failed to set rtla to the house keeping CPUs\n");816			goto out_err;817		}818	} else if (params->cpus) {819		/*820		 * Even if the user do not set a house-keeping CPU, try to821		 * move rtla to a CPU set different to the one where the user822		 * set the workload to run.823		 *824		 * No need to check results as this is an automatic attempt.825		 */826		auto_house_keeping(&params->monitored_cpus);827	}828 829	/*830	 * If the user did not specify a type of thread, try user-threads first.831	 * Fall back to kernel threads otherwise.832	 */833	if (!params->kernel_workload && !params->user_workload) {834		retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd");835		if (retval) {836			debug_msg("User-space interface detected, setting user-threads\n");837			params->user_workload = 1;838			params->user_top = 1;839		} else {840			debug_msg("User-space interface not detected, setting kernel-threads\n");841			params->kernel_workload = 1;842		}843	}844 845	if (params->user_top) {846		retval = osnoise_set_workload(top->context, 0);847		if (retval) {848			err_msg("Failed to set OSNOISE_WORKLOAD option\n");849			goto out_err;850		}851	}852 853	if (isatty(1) && !params->quiet)854		params->pretty_output = 1;855 856	return 0;857 858out_err:859	return -1;860}861 862/*863 * timerlat_init_top - initialize a timerlat top tool with parameters864 */865static struct osnoise_tool866*timerlat_init_top(struct timerlat_top_params *params)867{868	struct osnoise_tool *top;869	int nr_cpus;870 871	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);872 873	top = osnoise_init_tool("timerlat_top");874	if (!top)875		return NULL;876 877	top->data = timerlat_alloc_top(nr_cpus);878	if (!top->data)879		goto out_err;880 881	top->params = params;882 883	tep_register_event_handler(top->trace.tep, -1, "ftrace", "timerlat",884				   timerlat_top_handler, top);885 886	return top;887 888out_err:889	osnoise_destroy_tool(top);890	return NULL;891}892 893static int stop_tracing;894static void stop_top(int sig)895{896	stop_tracing = 1;897}898 899/*900 * timerlat_top_set_signals - handles the signal to stop the tool901 */902static void903timerlat_top_set_signals(struct timerlat_top_params *params)904{905	signal(SIGINT, stop_top);906	if (params->duration) {907		signal(SIGALRM, stop_top);908		alarm(params->duration);909	}910}911 912int timerlat_top_main(int argc, char *argv[])913{914	struct timerlat_top_params *params;915	struct osnoise_tool *record = NULL;916	struct timerlat_u_params params_u;917	struct osnoise_tool *top = NULL;918	struct osnoise_tool *aa = NULL;919	struct trace_instance *trace;920	int dma_latency_fd = -1;921	pthread_t timerlat_u;922	int return_value = 1;923	char *max_lat;924	int retval;925 926	params = timerlat_top_parse_args(argc, argv);927	if (!params)928		exit(1);929 930	top = timerlat_init_top(params);931	if (!top) {932		err_msg("Could not init osnoise top\n");933		goto out_exit;934	}935 936	retval = timerlat_top_apply_config(top, params);937	if (retval) {938		err_msg("Could not apply config\n");939		goto out_free;940	}941 942	trace = &top->trace;943 944	retval = enable_timerlat(trace);945	if (retval) {946		err_msg("Failed to enable timerlat tracer\n");947		goto out_free;948	}949 950	if (params->set_sched) {951		retval = set_comm_sched_attr("timerlat/", &params->sched_param);952		if (retval) {953			err_msg("Failed to set sched parameters\n");954			goto out_free;955		}956	}957 958	if (params->cgroup && !params->user_top) {959		retval = set_comm_cgroup("timerlat/", params->cgroup_name);960		if (!retval) {961			err_msg("Failed to move threads to cgroup\n");962			goto out_free;963		}964	}965 966	if (params->dma_latency >= 0) {967		dma_latency_fd = set_cpu_dma_latency(params->dma_latency);968		if (dma_latency_fd < 0) {969			err_msg("Could not set /dev/cpu_dma_latency.\n");970			goto out_free;971		}972	}973 974	if (params->trace_output) {975		record = osnoise_init_trace_tool("timerlat");976		if (!record) {977			err_msg("Failed to enable the trace instance\n");978			goto out_free;979		}980 981		if (params->events) {982			retval = trace_events_enable(&record->trace, params->events);983			if (retval)984				goto out_top;985		}986 987		if (params->buffer_size > 0) {988			retval = trace_set_buffer_size(&record->trace, params->buffer_size);989			if (retval)990				goto out_top;991		}992	}993 994	if (!params->no_aa) {995		if (params->aa_only) {996			/* as top is not used for display, use it for aa */997			aa = top;998		} else  {999			/* otherwise, a new instance is needed */1000			aa = osnoise_init_tool("timerlat_aa");1001			if (!aa)1002				goto out_top;1003		}1004 1005		retval = timerlat_aa_init(aa, params->dump_tasks);1006		if (retval) {1007			err_msg("Failed to enable the auto analysis instance\n");1008			goto out_top;1009		}1010 1011		/* if it is re-using the main instance, there is no need to start it */1012		if (aa != top) {1013			retval = enable_timerlat(&aa->trace);1014			if (retval) {1015				err_msg("Failed to enable timerlat tracer\n");1016				goto out_top;1017			}1018		}1019	}1020 1021	if (params->user_workload) {1022		/* rtla asked to stop */1023		params_u.should_run = 1;1024		/* all threads left */1025		params_u.stopped_running = 0;1026 1027		params_u.set = &params->monitored_cpus;1028		if (params->set_sched)1029			params_u.sched_param = &params->sched_param;1030		else1031			params_u.sched_param = NULL;1032 1033		params_u.cgroup_name = params->cgroup_name;1034 1035		retval = pthread_create(&timerlat_u, NULL, timerlat_u_dispatcher, &params_u);1036		if (retval)1037			err_msg("Error creating timerlat user-space threads\n");1038	}1039 1040	if (params->warmup > 0) {1041		debug_msg("Warming up for %d seconds\n", params->warmup);1042		sleep(params->warmup);1043	}1044 1045	/*1046	 * Start the tracers here, after having set all instances.1047	 *1048	 * Let the trace instance start first for the case of hitting a stop1049	 * tracing while enabling other instances. The trace instance is the1050	 * one with most valuable information.1051	 */1052	if (params->trace_output)1053		trace_instance_start(&record->trace);1054	if (!params->no_aa && aa != top)1055		trace_instance_start(&aa->trace);1056	trace_instance_start(trace);1057 1058	top->start_time = time(NULL);1059	timerlat_top_set_signals(params);1060 1061	while (!stop_tracing) {1062		sleep(params->sleep_time);1063 1064		if (params->aa_only && !trace_is_off(&top->trace, &record->trace))1065			continue;1066 1067		retval = tracefs_iterate_raw_events(trace->tep,1068						    trace->inst,1069						    NULL,1070						    0,1071						    collect_registered_events,1072						    trace);1073		if (retval < 0) {1074			err_msg("Error iterating on events\n");1075			goto out_top;1076		}1077 1078		if (!params->quiet)1079			timerlat_print_stats(params, top);1080 1081		if (trace_is_off(&top->trace, &record->trace))1082			break;1083 1084		/* is there still any user-threads ? */1085		if (params->user_workload) {1086			if (params_u.stopped_running) {1087				debug_msg("timerlat user space threads stopped!\n");1088				break;1089			}1090		}1091	}1092 1093	if (params->user_workload && !params_u.stopped_running) {1094		params_u.should_run = 0;1095		sleep(1);1096	}1097 1098	timerlat_print_stats(params, top);1099 1100	return_value = 0;1101 1102	if (trace_is_off(&top->trace, &record->trace)) {1103		printf("rtla timerlat hit stop tracing\n");1104 1105		if (!params->no_aa)1106			timerlat_auto_analysis(params->stop_us, params->stop_total_us);1107 1108		if (params->trace_output) {1109			printf("  Saving trace to %s\n", params->trace_output);1110			save_trace_to_file(record->trace.inst, params->trace_output);1111		}1112	} else if (params->aa_only) {1113		/*1114		 * If the trace did not stop with --aa-only, at least print the1115		 * max known latency.1116		 */1117		max_lat = tracefs_instance_file_read(trace->inst, "tracing_max_latency", NULL);1118		if (max_lat) {1119			printf("  Max latency was %s\n", max_lat);1120			free(max_lat);1121		}1122	}1123 1124out_top:1125	timerlat_aa_destroy();1126	if (dma_latency_fd >= 0)1127		close(dma_latency_fd);1128	trace_events_destroy(&record->trace, params->events);1129	params->events = NULL;1130out_free:1131	timerlat_free_top(top->data);1132	if (aa && aa != top)1133		osnoise_destroy_tool(aa);1134	osnoise_destroy_tool(record);1135	osnoise_destroy_tool(top);1136	free(params);1137out_exit:1138	exit(return_value);1139}1140