Linux Audio

Check our new training course

Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
  4 */
  5
 
  6#include <getopt.h>
  7#include <stdlib.h>
  8#include <string.h>
  9#include <signal.h>
 10#include <unistd.h>
 11#include <stdio.h>
 12#include <time.h>
 
 
 13
 14#include "utils.h"
 15#include "osnoise.h"
 16#include "timerlat.h"
 
 
 17
 18struct timerlat_hist_params {
 19	char			*cpus;
 20	char			*monitored_cpus;
 21	char			*trace_output;
 
 22	unsigned long long	runtime;
 23	long long		stop_us;
 24	long long		stop_total_us;
 25	long long		timerlat_period_us;
 26	long long		print_stack;
 27	int			sleep_time;
 28	int			output_divisor;
 29	int			duration;
 30	int			set_sched;
 31	int			dma_latency;
 
 
 
 
 
 
 
 
 32	struct sched_attr	sched_param;
 33	struct trace_events	*events;
 34
 35	char			no_irq;
 36	char			no_thread;
 37	char			no_header;
 38	char			no_summary;
 39	char			no_index;
 40	char			with_zeros;
 41	int			bucket_size;
 42	int			entries;
 
 
 
 43};
 44
 45struct timerlat_hist_cpu {
 46	int			*irq;
 47	int			*thread;
 
 48
 49	int			irq_count;
 50	int			thread_count;
 
 51
 52	unsigned long long	min_irq;
 53	unsigned long long	sum_irq;
 54	unsigned long long	max_irq;
 55
 56	unsigned long long	min_thread;
 57	unsigned long long	sum_thread;
 58	unsigned long long	max_thread;
 
 
 
 
 59};
 60
 61struct timerlat_hist_data {
 62	struct timerlat_hist_cpu	*hist;
 63	int				entries;
 64	int				bucket_size;
 65	int				nr_cpus;
 66};
 67
 68/*
 69 * timerlat_free_histogram - free runtime data
 70 */
 71static void
 72timerlat_free_histogram(struct timerlat_hist_data *data)
 73{
 74	int cpu;
 75
 76	/* one histogram for IRQ and one for thread, per CPU */
 77	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 78		if (data->hist[cpu].irq)
 79			free(data->hist[cpu].irq);
 80
 81		if (data->hist[cpu].thread)
 82			free(data->hist[cpu].thread);
 
 
 
 
 83	}
 84
 85	/* one set of histograms per CPU */
 86	if (data->hist)
 87		free(data->hist);
 88
 89	free(data);
 90}
 91
 92/*
 93 * timerlat_alloc_histogram - alloc runtime data
 94 */
 95static struct timerlat_hist_data
 96*timerlat_alloc_histogram(int nr_cpus, int entries, int bucket_size)
 97{
 98	struct timerlat_hist_data *data;
 99	int cpu;
100
101	data = calloc(1, sizeof(*data));
102	if (!data)
103		return NULL;
104
105	data->entries = entries;
106	data->bucket_size = bucket_size;
107	data->nr_cpus = nr_cpus;
108
109	/* one set of histograms per CPU */
110	data->hist = calloc(1, sizeof(*data->hist) * nr_cpus);
111	if (!data->hist)
112		goto cleanup;
113
114	/* one histogram for IRQ and one for thread, per cpu */
115	for (cpu = 0; cpu < nr_cpus; cpu++) {
116		data->hist[cpu].irq = calloc(1, sizeof(*data->hist->irq) * (entries + 1));
117		if (!data->hist[cpu].irq)
118			goto cleanup;
 
119		data->hist[cpu].thread = calloc(1, sizeof(*data->hist->thread) * (entries + 1));
120		if (!data->hist[cpu].thread)
121			goto cleanup;
 
 
 
 
122	}
123
124	/* set the min to max */
125	for (cpu = 0; cpu < nr_cpus; cpu++) {
126		data->hist[cpu].min_irq = ~0;
127		data->hist[cpu].min_thread = ~0;
 
128	}
129
130	return data;
131
132cleanup:
133	timerlat_free_histogram(data);
134	return NULL;
135}
136
137/*
138 * timerlat_hist_update - record a new timerlat occurent on cpu, updating data
139 */
140static void
141timerlat_hist_update(struct osnoise_tool *tool, int cpu,
142		     unsigned long long thread,
143		     unsigned long long latency)
144{
145	struct timerlat_hist_params *params = tool->params;
146	struct timerlat_hist_data *data = tool->data;
147	int entries = data->entries;
148	int bucket;
149	int *hist;
150
151	if (params->output_divisor)
152		latency = latency / params->output_divisor;
153
154	if (data->bucket_size)
155		bucket = latency / data->bucket_size;
156
157	if (!thread) {
158		hist = data->hist[cpu].irq;
159		data->hist[cpu].irq_count++;
160		update_min(&data->hist[cpu].min_irq, &latency);
161		update_sum(&data->hist[cpu].sum_irq, &latency);
162		update_max(&data->hist[cpu].max_irq, &latency);
163	} else {
164		hist = data->hist[cpu].thread;
165		data->hist[cpu].thread_count++;
166		update_min(&data->hist[cpu].min_thread, &latency);
167		update_sum(&data->hist[cpu].sum_thread, &latency);
168		update_max(&data->hist[cpu].max_thread, &latency);
 
 
 
 
 
 
169	}
170
171	if (bucket < entries)
172		hist[bucket]++;
173	else
174		hist[entries]++;
175}
176
177/*
178 * timerlat_hist_handler - this is the handler for timerlat tracer events
179 */
180static int
181timerlat_hist_handler(struct trace_seq *s, struct tep_record *record,
182		     struct tep_event *event, void *data)
183{
184	struct trace_instance *trace = data;
185	unsigned long long thread, latency;
186	struct osnoise_tool *tool;
187	int cpu = record->cpu;
188
189	tool = container_of(trace, struct osnoise_tool, trace);
190
191	tep_get_field_val(s, event, "context", record, &thread, 1);
192	tep_get_field_val(s, event, "timer_latency", record, &latency, 1);
193
194	timerlat_hist_update(tool, cpu, thread, latency);
195
196	return 0;
197}
198
199/*
200 * timerlat_hist_header - print the header of the tracer to the output
201 */
202static void timerlat_hist_header(struct osnoise_tool *tool)
203{
204	struct timerlat_hist_params *params = tool->params;
205	struct timerlat_hist_data *data = tool->data;
206	struct trace_seq *s = tool->trace.seq;
207	char duration[26];
208	int cpu;
209
210	if (params->no_header)
211		return;
212
213	get_duration(tool->start_time, duration, sizeof(duration));
214	trace_seq_printf(s, "# RTLA timerlat histogram\n");
215	trace_seq_printf(s, "# Time unit is %s (%s)\n",
216			params->output_divisor == 1 ? "nanoseconds" : "microseconds",
217			params->output_divisor == 1 ? "ns" : "us");
218
219	trace_seq_printf(s, "# Duration: %s\n", duration);
220
221	if (!params->no_index)
222		trace_seq_printf(s, "Index");
223
224	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
225		if (params->cpus && !params->monitored_cpus[cpu])
226			continue;
227
228		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
229			continue;
230
231		if (!params->no_irq)
232			trace_seq_printf(s, "   IRQ-%03d", cpu);
233
234		if (!params->no_thread)
235			trace_seq_printf(s, "   Thr-%03d", cpu);
 
 
 
236	}
237	trace_seq_printf(s, "\n");
238
239
240	trace_seq_do_printf(s);
241	trace_seq_reset(s);
242}
243
244/*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245 * timerlat_print_summary - print the summary of the hist data to the output
246 */
247static void
248timerlat_print_summary(struct timerlat_hist_params *params,
249		       struct trace_instance *trace,
250		       struct timerlat_hist_data *data)
251{
252	int cpu;
253
254	if (params->no_summary)
255		return;
256
257	if (!params->no_index)
258		trace_seq_printf(trace->seq, "count:");
259
260	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
261		if (params->cpus && !params->monitored_cpus[cpu])
262			continue;
263
264		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
265			continue;
266
267		if (!params->no_irq)
268			trace_seq_printf(trace->seq, "%9d ",
269					data->hist[cpu].irq_count);
270
271		if (!params->no_thread)
272			trace_seq_printf(trace->seq, "%9d ",
273					data->hist[cpu].thread_count);
 
 
 
 
274	}
275	trace_seq_printf(trace->seq, "\n");
276
277	if (!params->no_index)
278		trace_seq_printf(trace->seq, "min:  ");
279
280	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
281		if (params->cpus && !params->monitored_cpus[cpu])
282			continue;
283
284		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
285			continue;
286
287		if (!params->no_irq)
288			trace_seq_printf(trace->seq, "%9llu ",
289					data->hist[cpu].min_irq);
 
 
290
291		if (!params->no_thread)
292			trace_seq_printf(trace->seq, "%9llu ",
293					data->hist[cpu].min_thread);
 
 
 
 
 
 
 
 
294	}
295	trace_seq_printf(trace->seq, "\n");
296
297	if (!params->no_index)
298		trace_seq_printf(trace->seq, "avg:  ");
299
300	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
301		if (params->cpus && !params->monitored_cpus[cpu])
302			continue;
303
304		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
305			continue;
306
307		if (!params->no_irq) {
308			if (data->hist[cpu].irq_count)
309				trace_seq_printf(trace->seq, "%9llu ",
310						 data->hist[cpu].sum_irq / data->hist[cpu].irq_count);
311			else
312				trace_seq_printf(trace->seq, "        - ");
313		}
314
315		if (!params->no_thread) {
316			if (data->hist[cpu].thread_count)
317				trace_seq_printf(trace->seq, "%9llu ",
318						data->hist[cpu].sum_thread / data->hist[cpu].thread_count);
319			else
320				trace_seq_printf(trace->seq, "        - ");
321		}
 
 
 
 
322	}
323	trace_seq_printf(trace->seq, "\n");
324
325	if (!params->no_index)
326		trace_seq_printf(trace->seq, "max:  ");
327
328	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
329		if (params->cpus && !params->monitored_cpus[cpu])
330			continue;
331
332		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
333			continue;
334
335		if (!params->no_irq)
336			trace_seq_printf(trace->seq, "%9llu ",
337					data->hist[cpu].max_irq);
 
 
338
339		if (!params->no_thread)
340			trace_seq_printf(trace->seq, "%9llu ",
341					data->hist[cpu].max_thread);
 
 
 
 
 
 
 
 
342	}
343	trace_seq_printf(trace->seq, "\n");
344	trace_seq_do_printf(trace->seq);
345	trace_seq_reset(trace->seq);
346}
347
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348/*
349 * timerlat_print_stats - print data for all CPUs
350 */
351static void
352timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *tool)
353{
354	struct timerlat_hist_data *data = tool->data;
355	struct trace_instance *trace = &tool->trace;
356	int bucket, cpu;
357	int total;
358
359	timerlat_hist_header(tool);
360
361	for (bucket = 0; bucket < data->entries; bucket++) {
362		total = 0;
363
364		if (!params->no_index)
365			trace_seq_printf(trace->seq, "%-6d",
366					 bucket * data->bucket_size);
367
368		for (cpu = 0; cpu < data->nr_cpus; cpu++) {
369			if (params->cpus && !params->monitored_cpus[cpu])
370				continue;
371
372			if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
373				continue;
374
375			if (!params->no_irq) {
376				total += data->hist[cpu].irq[bucket];
377				trace_seq_printf(trace->seq, "%9d ",
378						data->hist[cpu].irq[bucket]);
379			}
380
381			if (!params->no_thread) {
382				total += data->hist[cpu].thread[bucket];
383				trace_seq_printf(trace->seq, "%9d ",
384						data->hist[cpu].thread[bucket]);
385			}
386
 
 
 
 
 
 
387		}
388
389		if (total == 0 && !params->with_zeros) {
390			trace_seq_reset(trace->seq);
391			continue;
392		}
393
394		trace_seq_printf(trace->seq, "\n");
395		trace_seq_do_printf(trace->seq);
396		trace_seq_reset(trace->seq);
397	}
398
399	if (!params->no_index)
400		trace_seq_printf(trace->seq, "over: ");
401
402	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
403		if (params->cpus && !params->monitored_cpus[cpu])
404			continue;
405
406		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
407			continue;
408
409		if (!params->no_irq)
410			trace_seq_printf(trace->seq, "%9d ",
411					 data->hist[cpu].irq[data->entries]);
412
413		if (!params->no_thread)
414			trace_seq_printf(trace->seq, "%9d ",
415					 data->hist[cpu].thread[data->entries]);
 
 
 
 
416	}
417	trace_seq_printf(trace->seq, "\n");
418	trace_seq_do_printf(trace->seq);
419	trace_seq_reset(trace->seq);
420
421	timerlat_print_summary(params, trace, data);
 
422}
423
424/*
425 * timerlat_hist_usage - prints timerlat top usage message
426 */
427static void timerlat_hist_usage(char *usage)
428{
429	int i;
430
431	char *msg[] = {
432		"",
433		"  usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\",
434		"         [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] \\",
435		"	  [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",
436		"	  [--no-index] [--with-zeros] [--dma-latency us]",
 
437		"",
438		"	  -h/--help: print this menu",
439		"	  -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
440		"	  -p/--period us: timerlat period in us",
441		"	  -i/--irq us: stop trace if the irq latency is higher than the argument in us",
442		"	  -T/--thread us: stop trace if the thread latency is higher than the argument in us",
443		"	  -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us",
444		"	  -c/--cpus cpus: run the tracer only on the given cpus",
 
 
445		"	  -d/--duration time[m|h|d]: duration of the session in seconds",
 
446		"	  -D/--debug: print debug info",
447		"	  -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]",
448		"	  -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
449		"	     --filter <filter>: enable a trace event filter to the previous -e event",
450		"	     --trigger <trigger>: enable a trace event trigger to the previous -e event",
451		"	  -n/--nano: display data in nanoseconds",
 
452		"	  -b/--bucket-size N: set the histogram bucket size (default 1)",
453		"	  -E/--entries N: set the number of entries of the histogram (default 256)",
454		"	     --no-irq: ignore IRQ latencies",
455		"	     --no-thread: ignore thread latencies",
456		"	     --no-header: do not print header",
457		"	     --no-summary: do not print summary",
458		"	     --no-index: do not print index",
459		"	     --with-zeros: print zero only entries",
460		"	     --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",
461		"	  -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
462		"		o:prio - use SCHED_OTHER with prio",
463		"		r:prio - use SCHED_RR with prio",
464		"		f:prio - use SCHED_FIFO with prio",
465		"		d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
466		"						       in nanoseconds",
 
 
 
 
 
 
467		NULL,
468	};
469
470	if (usage)
471		fprintf(stderr, "%s\n", usage);
472
473	fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n",
474			VERSION);
475
476	for (i = 0; msg[i]; i++)
477		fprintf(stderr, "%s\n", msg[i]);
478	exit(1);
 
 
 
 
479}
480
481/*
482 * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters
483 */
484static struct timerlat_hist_params
485*timerlat_hist_parse_args(int argc, char *argv[])
486{
487	struct timerlat_hist_params *params;
488	struct trace_events *tevent;
489	int auto_thresh;
490	int retval;
491	int c;
492
493	params = calloc(1, sizeof(*params));
494	if (!params)
495		exit(1);
496
497	/* disabled by default */
498	params->dma_latency = -1;
499
 
 
 
500	/* display data in microseconds */
501	params->output_divisor = 1000;
502	params->bucket_size = 1;
503	params->entries = 256;
504
505	while (1) {
506		static struct option long_options[] = {
507			{"auto",		required_argument,	0, 'a'},
508			{"cpus",		required_argument,	0, 'c'},
 
509			{"bucket-size",		required_argument,	0, 'b'},
510			{"debug",		no_argument,		0, 'D'},
511			{"entries",		required_argument,	0, 'E'},
512			{"duration",		required_argument,	0, 'd'},
 
513			{"help",		no_argument,		0, 'h'},
514			{"irq",			required_argument,	0, 'i'},
515			{"nano",		no_argument,		0, 'n'},
516			{"period",		required_argument,	0, 'p'},
517			{"priority",		required_argument,	0, 'P'},
518			{"stack",		required_argument,	0, 's'},
519			{"thread",		required_argument,	0, 'T'},
520			{"trace",		optional_argument,	0, 't'},
 
 
 
521			{"event",		required_argument,	0, 'e'},
522			{"no-irq",		no_argument,		0, '0'},
523			{"no-thread",		no_argument,		0, '1'},
524			{"no-header",		no_argument,		0, '2'},
525			{"no-summary",		no_argument,		0, '3'},
526			{"no-index",		no_argument,		0, '4'},
527			{"with-zeros",		no_argument,		0, '5'},
528			{"trigger",		required_argument,	0, '6'},
529			{"filter",		required_argument,	0, '7'},
530			{"dma-latency",		required_argument,	0, '8'},
 
 
 
 
 
531			{0, 0, 0, 0}
532		};
533
534		/* getopt_long stores the option index here. */
535		int option_index = 0;
536
537		c = getopt_long(argc, argv, "a:c:b:d:e:E:Dhi:np:P:s:t::T:0123456:7:8:",
538				 long_options, &option_index);
539
540		/* detect the end of the options. */
541		if (c == -1)
542			break;
543
544		switch (c) {
545		case 'a':
546			auto_thresh = get_llong_from_str(optarg);
547
548			/* set thread stop to auto_thresh */
549			params->stop_total_us = auto_thresh;
 
550
551			/* get stack trace */
552			params->print_stack = auto_thresh;
553
554			/* set trace */
555			params->trace_output = "timerlat_trace.txt";
556
557			break;
558		case 'c':
559			retval = parse_cpu_list(optarg, &params->monitored_cpus);
560			if (retval)
561				timerlat_hist_usage("\nInvalid -c cpu list\n");
562			params->cpus = optarg;
563			break;
 
 
 
 
 
 
 
 
 
 
564		case 'b':
565			params->bucket_size = get_llong_from_str(optarg);
566			if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))
567				timerlat_hist_usage("Bucket size needs to be > 0 and <= 1000000\n");
568			break;
569		case 'D':
570			config_debug = 1;
571			break;
572		case 'd':
573			params->duration = parse_seconds_duration(optarg);
574			if (!params->duration)
575				timerlat_hist_usage("Invalid -D duration\n");
576			break;
577		case 'e':
578			tevent = trace_event_alloc(optarg);
579			if (!tevent) {
580				err_msg("Error alloc trace event");
581				exit(EXIT_FAILURE);
582			}
583
584			if (params->events)
585				tevent->next = params->events;
586
587			params->events = tevent;
588			break;
589		case 'E':
590			params->entries = get_llong_from_str(optarg);
591			if ((params->entries < 10) || (params->entries > 9999999))
592					timerlat_hist_usage("Entries must be > 10 and < 9999999\n");
593			break;
594		case 'h':
595		case '?':
596			timerlat_hist_usage(NULL);
597			break;
 
 
 
 
 
 
 
 
598		case 'i':
599			params->stop_us = get_llong_from_str(optarg);
600			break;
 
 
 
601		case 'n':
602			params->output_divisor = 1;
603			break;
604		case 'p':
605			params->timerlat_period_us = get_llong_from_str(optarg);
606			if (params->timerlat_period_us > 1000000)
607				timerlat_hist_usage("Period longer than 1 s\n");
608			break;
609		case 'P':
610			retval = parse_prio(optarg, &params->sched_param);
611			if (retval == -1)
612				timerlat_hist_usage("Invalid -P priority");
613			params->set_sched = 1;
614			break;
615		case 's':
616			params->print_stack = get_llong_from_str(optarg);
617			break;
618		case 'T':
619			params->stop_total_us = get_llong_from_str(optarg);
620			break;
621		case 't':
622			if (optarg)
623				/* skip = */
624				params->trace_output = &optarg[1];
 
 
 
 
625			else
626				params->trace_output = "timerlat_trace.txt";
627			break;
 
 
 
 
 
 
628		case '0': /* no irq */
629			params->no_irq = 1;
630			break;
631		case '1': /* no thread */
632			params->no_thread = 1;
633			break;
634		case '2': /* no header */
635			params->no_header = 1;
636			break;
637		case '3': /* no summary */
638			params->no_summary = 1;
639			break;
640		case '4': /* no index */
641			params->no_index = 1;
642			break;
643		case '5': /* with zeros */
644			params->with_zeros = 1;
645			break;
646		case '6': /* trigger */
647			if (params->events) {
648				retval = trace_event_add_trigger(params->events, optarg);
649				if (retval) {
650					err_msg("Error adding trigger %s\n", optarg);
651					exit(EXIT_FAILURE);
652				}
653			} else {
654				timerlat_hist_usage("--trigger requires a previous -e\n");
655			}
656			break;
657		case '7': /* filter */
658			if (params->events) {
659				retval = trace_event_add_filter(params->events, optarg);
660				if (retval) {
661					err_msg("Error adding filter %s\n", optarg);
662					exit(EXIT_FAILURE);
663				}
664			} else {
665				timerlat_hist_usage("--filter requires a previous -e\n");
666			}
667			break;
668		case '8':
669			params->dma_latency = get_llong_from_str(optarg);
670			if (params->dma_latency < 0 || params->dma_latency > 10000) {
671				err_msg("--dma-latency needs to be >= 0 and < 10000");
672				exit(EXIT_FAILURE);
673			}
674			break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
675		default:
676			timerlat_hist_usage("Invalid option");
677		}
678	}
679
680	if (geteuid()) {
681		err_msg("rtla needs root permission\n");
682		exit(EXIT_FAILURE);
683	}
684
685	if (params->no_irq && params->no_thread)
686		timerlat_hist_usage("no-irq and no-thread set, there is nothing to do here");
687
688	if (params->no_index && !params->with_zeros)
689		timerlat_hist_usage("no-index set with with-zeros is not set - it does not make sense");
690
 
 
 
 
 
 
 
 
 
691	return params;
692}
693
694/*
695 * timerlat_hist_apply_config - apply the hist configs to the initialized tool
696 */
697static int
698timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_hist_params *params)
699{
700	int retval;
701
702	if (!params->sleep_time)
703		params->sleep_time = 1;
704
705	if (params->cpus) {
706		retval = osnoise_set_cpus(tool->context, params->cpus);
707		if (retval) {
708			err_msg("Failed to apply CPUs config\n");
709			goto out_err;
710		}
 
 
 
711	}
712
713	if (params->stop_us) {
714		retval = osnoise_set_stop_us(tool->context, params->stop_us);
715		if (retval) {
716			err_msg("Failed to set stop us\n");
717			goto out_err;
718		}
719	}
720
721	if (params->stop_total_us) {
722		retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
723		if (retval) {
724			err_msg("Failed to set stop total us\n");
725			goto out_err;
726		}
727	}
728
729	if (params->timerlat_period_us) {
730		retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us);
731		if (retval) {
732			err_msg("Failed to set timerlat period\n");
733			goto out_err;
734		}
735	}
736
737	if (params->print_stack) {
738		retval = osnoise_set_print_stack(tool->context, params->print_stack);
739		if (retval) {
740			err_msg("Failed to set print stack\n");
741			goto out_err;
742		}
743	}
744
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
745	return 0;
746
747out_err:
748	return -1;
749}
750
751/*
752 * timerlat_init_hist - initialize a timerlat hist tool with parameters
753 */
754static struct osnoise_tool
755*timerlat_init_hist(struct timerlat_hist_params *params)
756{
757	struct osnoise_tool *tool;
758	int nr_cpus;
759
760	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
761
762	tool = osnoise_init_tool("timerlat_hist");
763	if (!tool)
764		return NULL;
765
766	tool->data = timerlat_alloc_histogram(nr_cpus, params->entries, params->bucket_size);
767	if (!tool->data)
768		goto out_err;
769
770	tool->params = params;
771
772	tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat",
773				   timerlat_hist_handler, tool);
774
775	return tool;
776
777out_err:
778	osnoise_destroy_tool(tool);
779	return NULL;
780}
781
782static int stop_tracing;
 
783static void stop_hist(int sig)
784{
 
 
 
 
 
 
 
 
785	stop_tracing = 1;
 
 
786}
787
788/*
789 * timerlat_hist_set_signals - handles the signal to stop the tool
790 */
791static void
792timerlat_hist_set_signals(struct timerlat_hist_params *params)
793{
794	signal(SIGINT, stop_hist);
795	if (params->duration) {
796		signal(SIGALRM, stop_hist);
797		alarm(params->duration);
798	}
799}
800
801int timerlat_hist_main(int argc, char *argv[])
802{
803	struct timerlat_hist_params *params;
804	struct osnoise_tool *record = NULL;
 
805	struct osnoise_tool *tool = NULL;
 
806	struct trace_instance *trace;
807	int dma_latency_fd = -1;
808	int return_value = 1;
 
809	int retval;
 
810
811	params = timerlat_hist_parse_args(argc, argv);
812	if (!params)
813		exit(1);
814
815	tool = timerlat_init_hist(params);
816	if (!tool) {
817		err_msg("Could not init osnoise hist\n");
818		goto out_exit;
819	}
820
821	retval = timerlat_hist_apply_config(tool, params);
822	if (retval) {
823		err_msg("Could not apply config\n");
824		goto out_free;
825	}
826
827	trace = &tool->trace;
 
 
 
 
 
 
828
829	retval = enable_timerlat(trace);
830	if (retval) {
831		err_msg("Failed to enable timerlat tracer\n");
832		goto out_free;
833	}
834
835	if (params->set_sched) {
836		retval = set_comm_sched_attr("timerlat/", &params->sched_param);
837		if (retval) {
838			err_msg("Failed to set sched parameters\n");
839			goto out_free;
840		}
841	}
842
 
 
 
 
 
 
 
 
843	if (params->dma_latency >= 0) {
844		dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
845		if (dma_latency_fd < 0) {
846			err_msg("Could not set /dev/cpu_dma_latency.\n");
847			goto out_free;
848		}
849	}
850
851	trace_instance_start(trace);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
852
853	if (params->trace_output) {
854		record = osnoise_init_trace_tool("timerlat");
855		if (!record) {
856			err_msg("Failed to enable the trace instance\n");
857			goto out_free;
858		}
859
860		if (params->events) {
861			retval = trace_events_enable(&record->trace, params->events);
862			if (retval)
863				goto out_hist;
864		}
865
866		trace_instance_start(&record->trace);
 
 
 
 
867	}
868
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
869	tool->start_time = time(NULL);
870	timerlat_hist_set_signals(params);
871
872	while (!stop_tracing) {
873		sleep(params->sleep_time);
874
875		retval = tracefs_iterate_raw_events(trace->tep,
876						    trace->inst,
877						    NULL,
878						    0,
879						    collect_registered_events,
880						    trace);
881		if (retval < 0) {
882			err_msg("Error iterating on events\n");
883			goto out_hist;
884		}
885
886		if (trace_is_off(&tool->trace, &record->trace))
887			break;
 
 
 
 
 
 
 
 
 
 
 
 
 
888	}
889
890	timerlat_print_stats(params, tool);
891
892	return_value = 0;
893
894	if (trace_is_off(&tool->trace, &record->trace)) {
895		printf("rtla timerlat hit stop tracing\n");
 
 
 
 
896		if (params->trace_output) {
897			printf("  Saving trace to %s\n", params->trace_output);
898			save_trace_to_file(record->trace.inst, params->trace_output);
899		}
900	}
901
902out_hist:
 
903	if (dma_latency_fd >= 0)
904		close(dma_latency_fd);
 
 
 
 
 
 
 
905	trace_events_destroy(&record->trace, params->events);
906	params->events = NULL;
907out_free:
908	timerlat_free_histogram(tool->data);
 
909	osnoise_destroy_tool(record);
910	osnoise_destroy_tool(tool);
911	free(params);
 
912out_exit:
913	exit(return_value);
914}
v6.13.7
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
   4 */
   5
   6#define _GNU_SOURCE
   7#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 <sched.h>
  15#include <pthread.h>
  16
  17#include "utils.h"
  18#include "osnoise.h"
  19#include "timerlat.h"
  20#include "timerlat_aa.h"
  21#include "timerlat_u.h"
  22
  23struct timerlat_hist_params {
  24	char			*cpus;
  25	cpu_set_t		monitored_cpus;
  26	char			*trace_output;
  27	char			*cgroup_name;
  28	unsigned long long	runtime;
  29	long long		stop_us;
  30	long long		stop_total_us;
  31	long long		timerlat_period_us;
  32	long long		print_stack;
  33	int			sleep_time;
  34	int			output_divisor;
  35	int			duration;
  36	int			set_sched;
  37	int			dma_latency;
  38	int			cgroup;
  39	int			hk_cpus;
  40	int			no_aa;
  41	int			dump_tasks;
  42	int			user_workload;
  43	int			kernel_workload;
  44	int			user_hist;
  45	cpu_set_t		hk_cpu_set;
  46	struct sched_attr	sched_param;
  47	struct trace_events	*events;
 
  48	char			no_irq;
  49	char			no_thread;
  50	char			no_header;
  51	char			no_summary;
  52	char			no_index;
  53	char			with_zeros;
  54	int			bucket_size;
  55	int			entries;
  56	int			warmup;
  57	int			buffer_size;
  58	int			deepest_idle_state;
  59};
  60
  61struct timerlat_hist_cpu {
  62	int			*irq;
  63	int			*thread;
  64	int			*user;
  65
  66	unsigned long long	irq_count;
  67	unsigned long long	thread_count;
  68	unsigned long long	user_count;
  69
  70	unsigned long long	min_irq;
  71	unsigned long long	sum_irq;
  72	unsigned long long	max_irq;
  73
  74	unsigned long long	min_thread;
  75	unsigned long long	sum_thread;
  76	unsigned long long	max_thread;
  77
  78	unsigned long long	min_user;
  79	unsigned long long	sum_user;
  80	unsigned long long	max_user;
  81};
  82
  83struct timerlat_hist_data {
  84	struct timerlat_hist_cpu	*hist;
  85	int				entries;
  86	int				bucket_size;
  87	int				nr_cpus;
  88};
  89
  90/*
  91 * timerlat_free_histogram - free runtime data
  92 */
  93static void
  94timerlat_free_histogram(struct timerlat_hist_data *data)
  95{
  96	int cpu;
  97
  98	/* one histogram for IRQ and one for thread, per CPU */
  99	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 100		if (data->hist[cpu].irq)
 101			free(data->hist[cpu].irq);
 102
 103		if (data->hist[cpu].thread)
 104			free(data->hist[cpu].thread);
 105
 106		if (data->hist[cpu].user)
 107			free(data->hist[cpu].user);
 108
 109	}
 110
 111	/* one set of histograms per CPU */
 112	if (data->hist)
 113		free(data->hist);
 114
 115	free(data);
 116}
 117
 118/*
 119 * timerlat_alloc_histogram - alloc runtime data
 120 */
 121static struct timerlat_hist_data
 122*timerlat_alloc_histogram(int nr_cpus, int entries, int bucket_size)
 123{
 124	struct timerlat_hist_data *data;
 125	int cpu;
 126
 127	data = calloc(1, sizeof(*data));
 128	if (!data)
 129		return NULL;
 130
 131	data->entries = entries;
 132	data->bucket_size = bucket_size;
 133	data->nr_cpus = nr_cpus;
 134
 135	/* one set of histograms per CPU */
 136	data->hist = calloc(1, sizeof(*data->hist) * nr_cpus);
 137	if (!data->hist)
 138		goto cleanup;
 139
 140	/* one histogram for IRQ and one for thread, per cpu */
 141	for (cpu = 0; cpu < nr_cpus; cpu++) {
 142		data->hist[cpu].irq = calloc(1, sizeof(*data->hist->irq) * (entries + 1));
 143		if (!data->hist[cpu].irq)
 144			goto cleanup;
 145
 146		data->hist[cpu].thread = calloc(1, sizeof(*data->hist->thread) * (entries + 1));
 147		if (!data->hist[cpu].thread)
 148			goto cleanup;
 149
 150		data->hist[cpu].user = calloc(1, sizeof(*data->hist->user) * (entries + 1));
 151		if (!data->hist[cpu].user)
 152			goto cleanup;
 153	}
 154
 155	/* set the min to max */
 156	for (cpu = 0; cpu < nr_cpus; cpu++) {
 157		data->hist[cpu].min_irq = ~0;
 158		data->hist[cpu].min_thread = ~0;
 159		data->hist[cpu].min_user = ~0;
 160	}
 161
 162	return data;
 163
 164cleanup:
 165	timerlat_free_histogram(data);
 166	return NULL;
 167}
 168
 169/*
 170 * timerlat_hist_update - record a new timerlat occurent on cpu, updating data
 171 */
 172static void
 173timerlat_hist_update(struct osnoise_tool *tool, int cpu,
 174		     unsigned long long context,
 175		     unsigned long long latency)
 176{
 177	struct timerlat_hist_params *params = tool->params;
 178	struct timerlat_hist_data *data = tool->data;
 179	int entries = data->entries;
 180	int bucket;
 181	int *hist;
 182
 183	if (params->output_divisor)
 184		latency = latency / params->output_divisor;
 185
 186	bucket = latency / data->bucket_size;
 
 187
 188	if (!context) {
 189		hist = data->hist[cpu].irq;
 190		data->hist[cpu].irq_count++;
 191		update_min(&data->hist[cpu].min_irq, &latency);
 192		update_sum(&data->hist[cpu].sum_irq, &latency);
 193		update_max(&data->hist[cpu].max_irq, &latency);
 194	} else if (context == 1) {
 195		hist = data->hist[cpu].thread;
 196		data->hist[cpu].thread_count++;
 197		update_min(&data->hist[cpu].min_thread, &latency);
 198		update_sum(&data->hist[cpu].sum_thread, &latency);
 199		update_max(&data->hist[cpu].max_thread, &latency);
 200	} else { /* user */
 201		hist = data->hist[cpu].user;
 202		data->hist[cpu].user_count++;
 203		update_min(&data->hist[cpu].min_user, &latency);
 204		update_sum(&data->hist[cpu].sum_user, &latency);
 205		update_max(&data->hist[cpu].max_user, &latency);
 206	}
 207
 208	if (bucket < entries)
 209		hist[bucket]++;
 210	else
 211		hist[entries]++;
 212}
 213
 214/*
 215 * timerlat_hist_handler - this is the handler for timerlat tracer events
 216 */
 217static int
 218timerlat_hist_handler(struct trace_seq *s, struct tep_record *record,
 219		     struct tep_event *event, void *data)
 220{
 221	struct trace_instance *trace = data;
 222	unsigned long long context, latency;
 223	struct osnoise_tool *tool;
 224	int cpu = record->cpu;
 225
 226	tool = container_of(trace, struct osnoise_tool, trace);
 227
 228	tep_get_field_val(s, event, "context", record, &context, 1);
 229	tep_get_field_val(s, event, "timer_latency", record, &latency, 1);
 230
 231	timerlat_hist_update(tool, cpu, context, latency);
 232
 233	return 0;
 234}
 235
 236/*
 237 * timerlat_hist_header - print the header of the tracer to the output
 238 */
 239static void timerlat_hist_header(struct osnoise_tool *tool)
 240{
 241	struct timerlat_hist_params *params = tool->params;
 242	struct timerlat_hist_data *data = tool->data;
 243	struct trace_seq *s = tool->trace.seq;
 244	char duration[26];
 245	int cpu;
 246
 247	if (params->no_header)
 248		return;
 249
 250	get_duration(tool->start_time, duration, sizeof(duration));
 251	trace_seq_printf(s, "# RTLA timerlat histogram\n");
 252	trace_seq_printf(s, "# Time unit is %s (%s)\n",
 253			params->output_divisor == 1 ? "nanoseconds" : "microseconds",
 254			params->output_divisor == 1 ? "ns" : "us");
 255
 256	trace_seq_printf(s, "# Duration: %s\n", duration);
 257
 258	if (!params->no_index)
 259		trace_seq_printf(s, "Index");
 260
 261	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 262		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
 263			continue;
 264
 265		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
 266			continue;
 267
 268		if (!params->no_irq)
 269			trace_seq_printf(s, "   IRQ-%03d", cpu);
 270
 271		if (!params->no_thread)
 272			trace_seq_printf(s, "   Thr-%03d", cpu);
 273
 274		if (params->user_hist)
 275			trace_seq_printf(s, "   Usr-%03d", cpu);
 276	}
 277	trace_seq_printf(s, "\n");
 278
 279
 280	trace_seq_do_printf(s);
 281	trace_seq_reset(s);
 282}
 283
 284/*
 285 * format_summary_value - format a line of summary value (min, max or avg)
 286 * of hist data
 287 */
 288static void format_summary_value(struct trace_seq *seq,
 289				 int count,
 290				 unsigned long long val,
 291				 bool avg)
 292{
 293	if (count)
 294		trace_seq_printf(seq, "%9llu ", avg ? val / count : val);
 295	else
 296		trace_seq_printf(seq, "%9c ", '-');
 297}
 298
 299/*
 300 * timerlat_print_summary - print the summary of the hist data to the output
 301 */
 302static void
 303timerlat_print_summary(struct timerlat_hist_params *params,
 304		       struct trace_instance *trace,
 305		       struct timerlat_hist_data *data)
 306{
 307	int cpu;
 308
 309	if (params->no_summary)
 310		return;
 311
 312	if (!params->no_index)
 313		trace_seq_printf(trace->seq, "count:");
 314
 315	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 316		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
 317			continue;
 318
 319		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
 320			continue;
 321
 322		if (!params->no_irq)
 323			trace_seq_printf(trace->seq, "%9llu ",
 324					data->hist[cpu].irq_count);
 325
 326		if (!params->no_thread)
 327			trace_seq_printf(trace->seq, "%9llu ",
 328					data->hist[cpu].thread_count);
 329
 330		if (params->user_hist)
 331			trace_seq_printf(trace->seq, "%9llu ",
 332					 data->hist[cpu].user_count);
 333	}
 334	trace_seq_printf(trace->seq, "\n");
 335
 336	if (!params->no_index)
 337		trace_seq_printf(trace->seq, "min:  ");
 338
 339	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 340		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
 341			continue;
 342
 343		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
 344			continue;
 345
 346		if (!params->no_irq)
 347			format_summary_value(trace->seq,
 348					     data->hist[cpu].irq_count,
 349					     data->hist[cpu].min_irq,
 350					     false);
 351
 352		if (!params->no_thread)
 353			format_summary_value(trace->seq,
 354					     data->hist[cpu].thread_count,
 355					     data->hist[cpu].min_thread,
 356					     false);
 357
 358		if (params->user_hist)
 359			format_summary_value(trace->seq,
 360					     data->hist[cpu].user_count,
 361					     data->hist[cpu].min_user,
 362					     false);
 363	}
 364	trace_seq_printf(trace->seq, "\n");
 365
 366	if (!params->no_index)
 367		trace_seq_printf(trace->seq, "avg:  ");
 368
 369	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 370		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
 371			continue;
 372
 373		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
 374			continue;
 375
 376		if (!params->no_irq)
 377			format_summary_value(trace->seq,
 378					     data->hist[cpu].irq_count,
 379					     data->hist[cpu].sum_irq,
 380					     true);
 
 
 381
 382		if (!params->no_thread)
 383			format_summary_value(trace->seq,
 384					     data->hist[cpu].thread_count,
 385					     data->hist[cpu].sum_thread,
 386					     true);
 387
 388		if (params->user_hist)
 389			format_summary_value(trace->seq,
 390					     data->hist[cpu].user_count,
 391					     data->hist[cpu].sum_user,
 392					     true);
 393	}
 394	trace_seq_printf(trace->seq, "\n");
 395
 396	if (!params->no_index)
 397		trace_seq_printf(trace->seq, "max:  ");
 398
 399	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 400		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
 401			continue;
 402
 403		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
 404			continue;
 405
 406		if (!params->no_irq)
 407			format_summary_value(trace->seq,
 408					     data->hist[cpu].irq_count,
 409					     data->hist[cpu].max_irq,
 410					     false);
 411
 412		if (!params->no_thread)
 413			format_summary_value(trace->seq,
 414					     data->hist[cpu].thread_count,
 415					     data->hist[cpu].max_thread,
 416					     false);
 417
 418		if (params->user_hist)
 419			format_summary_value(trace->seq,
 420					     data->hist[cpu].user_count,
 421					     data->hist[cpu].max_user,
 422					     false);
 423	}
 424	trace_seq_printf(trace->seq, "\n");
 425	trace_seq_do_printf(trace->seq);
 426	trace_seq_reset(trace->seq);
 427}
 428
 429static void
 430timerlat_print_stats_all(struct timerlat_hist_params *params,
 431			 struct trace_instance *trace,
 432			 struct timerlat_hist_data *data)
 433{
 434	struct timerlat_hist_cpu *cpu_data;
 435	struct timerlat_hist_cpu sum;
 436	int cpu;
 437
 438	if (params->no_summary)
 439		return;
 440
 441	memset(&sum, 0, sizeof(sum));
 442	sum.min_irq = ~0;
 443	sum.min_thread = ~0;
 444	sum.min_user = ~0;
 445
 446	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 447		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
 448			continue;
 449
 450		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
 451			continue;
 452
 453		cpu_data = &data->hist[cpu];
 454
 455		sum.irq_count += cpu_data->irq_count;
 456		update_min(&sum.min_irq, &cpu_data->min_irq);
 457		update_sum(&sum.sum_irq, &cpu_data->sum_irq);
 458		update_max(&sum.max_irq, &cpu_data->max_irq);
 459
 460		sum.thread_count += cpu_data->thread_count;
 461		update_min(&sum.min_thread, &cpu_data->min_thread);
 462		update_sum(&sum.sum_thread, &cpu_data->sum_thread);
 463		update_max(&sum.max_thread, &cpu_data->max_thread);
 464
 465		sum.user_count += cpu_data->user_count;
 466		update_min(&sum.min_user, &cpu_data->min_user);
 467		update_sum(&sum.sum_user, &cpu_data->sum_user);
 468		update_max(&sum.max_user, &cpu_data->max_user);
 469	}
 470
 471	if (!params->no_index)
 472		trace_seq_printf(trace->seq, "ALL:  ");
 473
 474	if (!params->no_irq)
 475		trace_seq_printf(trace->seq, "      IRQ");
 476
 477	if (!params->no_thread)
 478		trace_seq_printf(trace->seq, "       Thr");
 479
 480	if (params->user_hist)
 481		trace_seq_printf(trace->seq, "       Usr");
 482
 483	trace_seq_printf(trace->seq, "\n");
 484
 485	if (!params->no_index)
 486		trace_seq_printf(trace->seq, "count:");
 487
 488	if (!params->no_irq)
 489		trace_seq_printf(trace->seq, "%9llu ",
 490				 sum.irq_count);
 491
 492	if (!params->no_thread)
 493		trace_seq_printf(trace->seq, "%9llu ",
 494				 sum.thread_count);
 495
 496	if (params->user_hist)
 497		trace_seq_printf(trace->seq, "%9llu ",
 498				 sum.user_count);
 499
 500	trace_seq_printf(trace->seq, "\n");
 501
 502	if (!params->no_index)
 503		trace_seq_printf(trace->seq, "min:  ");
 504
 505	if (!params->no_irq)
 506		format_summary_value(trace->seq,
 507				     sum.irq_count,
 508				     sum.min_irq,
 509				     false);
 510
 511	if (!params->no_thread)
 512		format_summary_value(trace->seq,
 513				     sum.thread_count,
 514				     sum.min_thread,
 515				     false);
 516
 517	if (params->user_hist)
 518		format_summary_value(trace->seq,
 519				     sum.user_count,
 520				     sum.min_user,
 521				     false);
 522
 523	trace_seq_printf(trace->seq, "\n");
 524
 525	if (!params->no_index)
 526		trace_seq_printf(trace->seq, "avg:  ");
 527
 528	if (!params->no_irq)
 529		format_summary_value(trace->seq,
 530				     sum.irq_count,
 531				     sum.sum_irq,
 532				     true);
 533
 534	if (!params->no_thread)
 535		format_summary_value(trace->seq,
 536				     sum.thread_count,
 537				     sum.sum_thread,
 538				     true);
 539
 540	if (params->user_hist)
 541		format_summary_value(trace->seq,
 542				     sum.user_count,
 543				     sum.sum_user,
 544				     true);
 545
 546	trace_seq_printf(trace->seq, "\n");
 547
 548	if (!params->no_index)
 549		trace_seq_printf(trace->seq, "max:  ");
 550
 551	if (!params->no_irq)
 552		format_summary_value(trace->seq,
 553				     sum.irq_count,
 554				     sum.max_irq,
 555				     false);
 556
 557	if (!params->no_thread)
 558		format_summary_value(trace->seq,
 559				     sum.thread_count,
 560				     sum.max_thread,
 561				     false);
 562
 563	if (params->user_hist)
 564		format_summary_value(trace->seq,
 565				     sum.user_count,
 566				     sum.max_user,
 567				     false);
 568
 569	trace_seq_printf(trace->seq, "\n");
 570	trace_seq_do_printf(trace->seq);
 571	trace_seq_reset(trace->seq);
 572}
 573
 574/*
 575 * timerlat_print_stats - print data for each CPUs
 576 */
 577static void
 578timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *tool)
 579{
 580	struct timerlat_hist_data *data = tool->data;
 581	struct trace_instance *trace = &tool->trace;
 582	int bucket, cpu;
 583	int total;
 584
 585	timerlat_hist_header(tool);
 586
 587	for (bucket = 0; bucket < data->entries; bucket++) {
 588		total = 0;
 589
 590		if (!params->no_index)
 591			trace_seq_printf(trace->seq, "%-6d",
 592					 bucket * data->bucket_size);
 593
 594		for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 595			if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
 596				continue;
 597
 598			if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
 599				continue;
 600
 601			if (!params->no_irq) {
 602				total += data->hist[cpu].irq[bucket];
 603				trace_seq_printf(trace->seq, "%9d ",
 604						data->hist[cpu].irq[bucket]);
 605			}
 606
 607			if (!params->no_thread) {
 608				total += data->hist[cpu].thread[bucket];
 609				trace_seq_printf(trace->seq, "%9d ",
 610						data->hist[cpu].thread[bucket]);
 611			}
 612
 613			if (params->user_hist) {
 614				total += data->hist[cpu].user[bucket];
 615				trace_seq_printf(trace->seq, "%9d ",
 616						data->hist[cpu].user[bucket]);
 617			}
 618
 619		}
 620
 621		if (total == 0 && !params->with_zeros) {
 622			trace_seq_reset(trace->seq);
 623			continue;
 624		}
 625
 626		trace_seq_printf(trace->seq, "\n");
 627		trace_seq_do_printf(trace->seq);
 628		trace_seq_reset(trace->seq);
 629	}
 630
 631	if (!params->no_index)
 632		trace_seq_printf(trace->seq, "over: ");
 633
 634	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
 635		if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
 636			continue;
 637
 638		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
 639			continue;
 640
 641		if (!params->no_irq)
 642			trace_seq_printf(trace->seq, "%9d ",
 643					 data->hist[cpu].irq[data->entries]);
 644
 645		if (!params->no_thread)
 646			trace_seq_printf(trace->seq, "%9d ",
 647					 data->hist[cpu].thread[data->entries]);
 648
 649		if (params->user_hist)
 650			trace_seq_printf(trace->seq, "%9d ",
 651					 data->hist[cpu].user[data->entries]);
 652	}
 653	trace_seq_printf(trace->seq, "\n");
 654	trace_seq_do_printf(trace->seq);
 655	trace_seq_reset(trace->seq);
 656
 657	timerlat_print_summary(params, trace, data);
 658	timerlat_print_stats_all(params, trace, data);
 659}
 660
 661/*
 662 * timerlat_hist_usage - prints timerlat top usage message
 663 */
 664static void timerlat_hist_usage(char *usage)
 665{
 666	int i;
 667
 668	char *msg[] = {
 669		"",
 670		"  usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\",
 671		"         [-t[file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",
 672		"	  [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",
 673		"	  [--no-index] [--with-zeros] [--dma-latency us] [-C[=cgroup_name]] [--no-aa] [--dump-task] [-u|-k]",
 674		"	  [--warm-up s] [--deepest-idle-state n]",
 675		"",
 676		"	  -h/--help: print this menu",
 677		"	  -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
 678		"	  -p/--period us: timerlat period in us",
 679		"	  -i/--irq us: stop trace if the irq latency is higher than the argument in us",
 680		"	  -T/--thread us: stop trace if the thread latency is higher than the argument in us",
 681		"	  -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us",
 682		"	  -c/--cpus cpus: run the tracer only on the given cpus",
 683		"	  -H/--house-keeping cpus: run rtla control threads only on the given cpus",
 684		"	  -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
 685		"	  -d/--duration time[m|h|d]: duration of the session in seconds",
 686		"	     --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)",
 687		"	  -D/--debug: print debug info",
 688		"	  -t/--trace[file]: save the stopped trace to [file|timerlat_trace.txt]",
 689		"	  -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
 690		"	     --filter <filter>: enable a trace event filter to the previous -e event",
 691		"	     --trigger <trigger>: enable a trace event trigger to the previous -e event",
 692		"	  -n/--nano: display data in nanoseconds",
 693		"	     --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage",
 694		"	  -b/--bucket-size N: set the histogram bucket size (default 1)",
 695		"	  -E/--entries N: set the number of entries of the histogram (default 256)",
 696		"	     --no-irq: ignore IRQ latencies",
 697		"	     --no-thread: ignore thread latencies",
 698		"	     --no-header: do not print header",
 699		"	     --no-summary: do not print summary",
 700		"	     --no-index: do not print index",
 701		"	     --with-zeros: print zero only entries",
 702		"	     --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",
 703		"	  -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
 704		"		o:prio - use SCHED_OTHER with prio",
 705		"		r:prio - use SCHED_RR with prio",
 706		"		f:prio - use SCHED_FIFO with prio",
 707		"		d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
 708		"						       in nanoseconds",
 709		"	  -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads",
 710		"	  -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads",
 711		"	  -U/--user-load: enable timerlat for user-defined user-space workload",
 712		"	     --warm-up s: let the workload run for s seconds before collecting data",
 713		"	     --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
 714		"	     --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency",
 715		NULL,
 716	};
 717
 718	if (usage)
 719		fprintf(stderr, "%s\n", usage);
 720
 721	fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n",
 722			VERSION);
 723
 724	for (i = 0; msg[i]; i++)
 725		fprintf(stderr, "%s\n", msg[i]);
 726
 727	if (usage)
 728		exit(EXIT_FAILURE);
 729
 730	exit(EXIT_SUCCESS);
 731}
 732
 733/*
 734 * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters
 735 */
 736static struct timerlat_hist_params
 737*timerlat_hist_parse_args(int argc, char *argv[])
 738{
 739	struct timerlat_hist_params *params;
 740	struct trace_events *tevent;
 741	int auto_thresh;
 742	int retval;
 743	int c;
 744
 745	params = calloc(1, sizeof(*params));
 746	if (!params)
 747		exit(1);
 748
 749	/* disabled by default */
 750	params->dma_latency = -1;
 751
 752	/* disabled by default */
 753	params->deepest_idle_state = -2;
 754
 755	/* display data in microseconds */
 756	params->output_divisor = 1000;
 757	params->bucket_size = 1;
 758	params->entries = 256;
 759
 760	while (1) {
 761		static struct option long_options[] = {
 762			{"auto",		required_argument,	0, 'a'},
 763			{"cpus",		required_argument,	0, 'c'},
 764			{"cgroup",		optional_argument,	0, 'C'},
 765			{"bucket-size",		required_argument,	0, 'b'},
 766			{"debug",		no_argument,		0, 'D'},
 767			{"entries",		required_argument,	0, 'E'},
 768			{"duration",		required_argument,	0, 'd'},
 769			{"house-keeping",	required_argument,	0, 'H'},
 770			{"help",		no_argument,		0, 'h'},
 771			{"irq",			required_argument,	0, 'i'},
 772			{"nano",		no_argument,		0, 'n'},
 773			{"period",		required_argument,	0, 'p'},
 774			{"priority",		required_argument,	0, 'P'},
 775			{"stack",		required_argument,	0, 's'},
 776			{"thread",		required_argument,	0, 'T'},
 777			{"trace",		optional_argument,	0, 't'},
 778			{"user-threads",	no_argument,		0, 'u'},
 779			{"kernel-threads",	no_argument,		0, 'k'},
 780			{"user-load",		no_argument,		0, 'U'},
 781			{"event",		required_argument,	0, 'e'},
 782			{"no-irq",		no_argument,		0, '0'},
 783			{"no-thread",		no_argument,		0, '1'},
 784			{"no-header",		no_argument,		0, '2'},
 785			{"no-summary",		no_argument,		0, '3'},
 786			{"no-index",		no_argument,		0, '4'},
 787			{"with-zeros",		no_argument,		0, '5'},
 788			{"trigger",		required_argument,	0, '6'},
 789			{"filter",		required_argument,	0, '7'},
 790			{"dma-latency",		required_argument,	0, '8'},
 791			{"no-aa",		no_argument,		0, '9'},
 792			{"dump-task",		no_argument,		0, '\1'},
 793			{"warm-up",		required_argument,	0, '\2'},
 794			{"trace-buffer-size",	required_argument,	0, '\3'},
 795			{"deepest-idle-state",	required_argument,	0, '\4'},
 796			{0, 0, 0, 0}
 797		};
 798
 799		/* getopt_long stores the option index here. */
 800		int option_index = 0;
 801
 802		c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:i:knp:P:s:t::T:uU0123456:7:8:9\1\2:\3:",
 803				 long_options, &option_index);
 804
 805		/* detect the end of the options. */
 806		if (c == -1)
 807			break;
 808
 809		switch (c) {
 810		case 'a':
 811			auto_thresh = get_llong_from_str(optarg);
 812
 813			/* set thread stop to auto_thresh */
 814			params->stop_total_us = auto_thresh;
 815			params->stop_us = auto_thresh;
 816
 817			/* get stack trace */
 818			params->print_stack = auto_thresh;
 819
 820			/* set trace */
 821			params->trace_output = "timerlat_trace.txt";
 822
 823			break;
 824		case 'c':
 825			retval = parse_cpu_set(optarg, &params->monitored_cpus);
 826			if (retval)
 827				timerlat_hist_usage("\nInvalid -c cpu list\n");
 828			params->cpus = optarg;
 829			break;
 830		case 'C':
 831			params->cgroup = 1;
 832			if (!optarg) {
 833				/* will inherit this cgroup */
 834				params->cgroup_name = NULL;
 835			} else if (*optarg == '=') {
 836				/* skip the = */
 837				params->cgroup_name = ++optarg;
 838			}
 839			break;
 840		case 'b':
 841			params->bucket_size = get_llong_from_str(optarg);
 842			if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))
 843				timerlat_hist_usage("Bucket size needs to be > 0 and <= 1000000\n");
 844			break;
 845		case 'D':
 846			config_debug = 1;
 847			break;
 848		case 'd':
 849			params->duration = parse_seconds_duration(optarg);
 850			if (!params->duration)
 851				timerlat_hist_usage("Invalid -D duration\n");
 852			break;
 853		case 'e':
 854			tevent = trace_event_alloc(optarg);
 855			if (!tevent) {
 856				err_msg("Error alloc trace event");
 857				exit(EXIT_FAILURE);
 858			}
 859
 860			if (params->events)
 861				tevent->next = params->events;
 862
 863			params->events = tevent;
 864			break;
 865		case 'E':
 866			params->entries = get_llong_from_str(optarg);
 867			if ((params->entries < 10) || (params->entries > 9999999))
 868					timerlat_hist_usage("Entries must be > 10 and < 9999999\n");
 869			break;
 870		case 'h':
 871		case '?':
 872			timerlat_hist_usage(NULL);
 873			break;
 874		case 'H':
 875			params->hk_cpus = 1;
 876			retval = parse_cpu_set(optarg, &params->hk_cpu_set);
 877			if (retval) {
 878				err_msg("Error parsing house keeping CPUs\n");
 879				exit(EXIT_FAILURE);
 880			}
 881			break;
 882		case 'i':
 883			params->stop_us = get_llong_from_str(optarg);
 884			break;
 885		case 'k':
 886			params->kernel_workload = 1;
 887			break;
 888		case 'n':
 889			params->output_divisor = 1;
 890			break;
 891		case 'p':
 892			params->timerlat_period_us = get_llong_from_str(optarg);
 893			if (params->timerlat_period_us > 1000000)
 894				timerlat_hist_usage("Period longer than 1 s\n");
 895			break;
 896		case 'P':
 897			retval = parse_prio(optarg, &params->sched_param);
 898			if (retval == -1)
 899				timerlat_hist_usage("Invalid -P priority");
 900			params->set_sched = 1;
 901			break;
 902		case 's':
 903			params->print_stack = get_llong_from_str(optarg);
 904			break;
 905		case 'T':
 906			params->stop_total_us = get_llong_from_str(optarg);
 907			break;
 908		case 't':
 909			if (optarg) {
 910				if (optarg[0] == '=')
 911					params->trace_output = &optarg[1];
 912				else
 913					params->trace_output = &optarg[0];
 914			} else if (optind < argc && argv[optind][0] != '-')
 915				params->trace_output = argv[optind];
 916			else
 917				params->trace_output = "timerlat_trace.txt";
 918			break;
 919		case 'u':
 920			params->user_workload = 1;
 921			/* fallback: -u implies in -U */
 922		case 'U':
 923			params->user_hist = 1;
 924			break;
 925		case '0': /* no irq */
 926			params->no_irq = 1;
 927			break;
 928		case '1': /* no thread */
 929			params->no_thread = 1;
 930			break;
 931		case '2': /* no header */
 932			params->no_header = 1;
 933			break;
 934		case '3': /* no summary */
 935			params->no_summary = 1;
 936			break;
 937		case '4': /* no index */
 938			params->no_index = 1;
 939			break;
 940		case '5': /* with zeros */
 941			params->with_zeros = 1;
 942			break;
 943		case '6': /* trigger */
 944			if (params->events) {
 945				retval = trace_event_add_trigger(params->events, optarg);
 946				if (retval) {
 947					err_msg("Error adding trigger %s\n", optarg);
 948					exit(EXIT_FAILURE);
 949				}
 950			} else {
 951				timerlat_hist_usage("--trigger requires a previous -e\n");
 952			}
 953			break;
 954		case '7': /* filter */
 955			if (params->events) {
 956				retval = trace_event_add_filter(params->events, optarg);
 957				if (retval) {
 958					err_msg("Error adding filter %s\n", optarg);
 959					exit(EXIT_FAILURE);
 960				}
 961			} else {
 962				timerlat_hist_usage("--filter requires a previous -e\n");
 963			}
 964			break;
 965		case '8':
 966			params->dma_latency = get_llong_from_str(optarg);
 967			if (params->dma_latency < 0 || params->dma_latency > 10000) {
 968				err_msg("--dma-latency needs to be >= 0 and < 10000");
 969				exit(EXIT_FAILURE);
 970			}
 971			break;
 972		case '9':
 973			params->no_aa = 1;
 974			break;
 975		case '\1':
 976			params->dump_tasks = 1;
 977			break;
 978		case '\2':
 979			params->warmup = get_llong_from_str(optarg);
 980			break;
 981		case '\3':
 982			params->buffer_size = get_llong_from_str(optarg);
 983			break;
 984		case '\4':
 985			params->deepest_idle_state = get_llong_from_str(optarg);
 986			break;
 987		default:
 988			timerlat_hist_usage("Invalid option");
 989		}
 990	}
 991
 992	if (geteuid()) {
 993		err_msg("rtla needs root permission\n");
 994		exit(EXIT_FAILURE);
 995	}
 996
 997	if (params->no_irq && params->no_thread)
 998		timerlat_hist_usage("no-irq and no-thread set, there is nothing to do here");
 999
1000	if (params->no_index && !params->with_zeros)
1001		timerlat_hist_usage("no-index set with with-zeros is not set - it does not make sense");
1002
1003	/*
1004	 * Auto analysis only happens if stop tracing, thus:
1005	 */
1006	if (!params->stop_us && !params->stop_total_us)
1007		params->no_aa = 1;
1008
1009	if (params->kernel_workload && params->user_workload)
1010		timerlat_hist_usage("--kernel-threads and --user-threads are mutually exclusive!");
1011
1012	return params;
1013}
1014
1015/*
1016 * timerlat_hist_apply_config - apply the hist configs to the initialized tool
1017 */
1018static int
1019timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_hist_params *params)
1020{
1021	int retval, i;
1022
1023	if (!params->sleep_time)
1024		params->sleep_time = 1;
1025
1026	if (params->cpus) {
1027		retval = osnoise_set_cpus(tool->context, params->cpus);
1028		if (retval) {
1029			err_msg("Failed to apply CPUs config\n");
1030			goto out_err;
1031		}
1032	} else {
1033		for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)
1034			CPU_SET(i, &params->monitored_cpus);
1035	}
1036
1037	if (params->stop_us) {
1038		retval = osnoise_set_stop_us(tool->context, params->stop_us);
1039		if (retval) {
1040			err_msg("Failed to set stop us\n");
1041			goto out_err;
1042		}
1043	}
1044
1045	if (params->stop_total_us) {
1046		retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
1047		if (retval) {
1048			err_msg("Failed to set stop total us\n");
1049			goto out_err;
1050		}
1051	}
1052
1053	if (params->timerlat_period_us) {
1054		retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us);
1055		if (retval) {
1056			err_msg("Failed to set timerlat period\n");
1057			goto out_err;
1058		}
1059	}
1060
1061	if (params->print_stack) {
1062		retval = osnoise_set_print_stack(tool->context, params->print_stack);
1063		if (retval) {
1064			err_msg("Failed to set print stack\n");
1065			goto out_err;
1066		}
1067	}
1068
1069	if (params->hk_cpus) {
1070		retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
1071					   &params->hk_cpu_set);
1072		if (retval == -1) {
1073			err_msg("Failed to set rtla to the house keeping CPUs\n");
1074			goto out_err;
1075		}
1076	} else if (params->cpus) {
1077		/*
1078		 * Even if the user do not set a house-keeping CPU, try to
1079		 * move rtla to a CPU set different to the one where the user
1080		 * set the workload to run.
1081		 *
1082		 * No need to check results as this is an automatic attempt.
1083		 */
1084		auto_house_keeping(&params->monitored_cpus);
1085	}
1086
1087	/*
1088	 * If the user did not specify a type of thread, try user-threads first.
1089	 * Fall back to kernel threads otherwise.
1090	 */
1091	if (!params->kernel_workload && !params->user_hist) {
1092		retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd");
1093		if (retval) {
1094			debug_msg("User-space interface detected, setting user-threads\n");
1095			params->user_workload = 1;
1096			params->user_hist = 1;
1097		} else {
1098			debug_msg("User-space interface not detected, setting kernel-threads\n");
1099			params->kernel_workload = 1;
1100		}
1101	}
1102
1103	/*
1104	* Set workload according to type of thread if the kernel supports it.
1105	* On kernels without support, user threads will have already failed
1106	* on missing timerlat_fd, and kernel threads do not need it.
1107	*/
1108	retval = osnoise_set_workload(tool->context, params->kernel_workload);
1109	if (retval < -1) {
1110		err_msg("Failed to set OSNOISE_WORKLOAD option\n");
1111		goto out_err;
1112	}
1113
1114	return 0;
1115
1116out_err:
1117	return -1;
1118}
1119
1120/*
1121 * timerlat_init_hist - initialize a timerlat hist tool with parameters
1122 */
1123static struct osnoise_tool
1124*timerlat_init_hist(struct timerlat_hist_params *params)
1125{
1126	struct osnoise_tool *tool;
1127	int nr_cpus;
1128
1129	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
1130
1131	tool = osnoise_init_tool("timerlat_hist");
1132	if (!tool)
1133		return NULL;
1134
1135	tool->data = timerlat_alloc_histogram(nr_cpus, params->entries, params->bucket_size);
1136	if (!tool->data)
1137		goto out_err;
1138
1139	tool->params = params;
1140
1141	tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat",
1142				   timerlat_hist_handler, tool);
1143
1144	return tool;
1145
1146out_err:
1147	osnoise_destroy_tool(tool);
1148	return NULL;
1149}
1150
1151static int stop_tracing;
1152static struct trace_instance *hist_inst = NULL;
1153static void stop_hist(int sig)
1154{
1155	if (stop_tracing) {
1156		/*
1157		 * Stop requested twice in a row; abort event processing and
1158		 * exit immediately
1159		 */
1160		tracefs_iterate_stop(hist_inst->inst);
1161		return;
1162	}
1163	stop_tracing = 1;
1164	if (hist_inst)
1165		trace_instance_stop(hist_inst);
1166}
1167
1168/*
1169 * timerlat_hist_set_signals - handles the signal to stop the tool
1170 */
1171static void
1172timerlat_hist_set_signals(struct timerlat_hist_params *params)
1173{
1174	signal(SIGINT, stop_hist);
1175	if (params->duration) {
1176		signal(SIGALRM, stop_hist);
1177		alarm(params->duration);
1178	}
1179}
1180
1181int timerlat_hist_main(int argc, char *argv[])
1182{
1183	struct timerlat_hist_params *params;
1184	struct osnoise_tool *record = NULL;
1185	struct timerlat_u_params params_u;
1186	struct osnoise_tool *tool = NULL;
1187	struct osnoise_tool *aa = NULL;
1188	struct trace_instance *trace;
1189	int dma_latency_fd = -1;
1190	int return_value = 1;
1191	pthread_t timerlat_u;
1192	int retval;
1193	int nr_cpus, i;
1194
1195	params = timerlat_hist_parse_args(argc, argv);
1196	if (!params)
1197		exit(1);
1198
1199	tool = timerlat_init_hist(params);
1200	if (!tool) {
1201		err_msg("Could not init osnoise hist\n");
1202		goto out_exit;
1203	}
1204
1205	retval = timerlat_hist_apply_config(tool, params);
1206	if (retval) {
1207		err_msg("Could not apply config\n");
1208		goto out_free;
1209	}
1210
1211	trace = &tool->trace;
1212	/*
1213	 * Save trace instance into global variable so that SIGINT can stop
1214	 * the timerlat tracer.
1215	 * Otherwise, rtla could loop indefinitely when overloaded.
1216	 */
1217	hist_inst = trace;
1218
1219	retval = enable_timerlat(trace);
1220	if (retval) {
1221		err_msg("Failed to enable timerlat tracer\n");
1222		goto out_free;
1223	}
1224
1225	if (params->set_sched) {
1226		retval = set_comm_sched_attr("timerlat/", &params->sched_param);
1227		if (retval) {
1228			err_msg("Failed to set sched parameters\n");
1229			goto out_free;
1230		}
1231	}
1232
1233	if (params->cgroup && !params->user_workload) {
1234		retval = set_comm_cgroup("timerlat/", params->cgroup_name);
1235		if (!retval) {
1236			err_msg("Failed to move threads to cgroup\n");
1237			goto out_free;
1238		}
1239	}
1240
1241	if (params->dma_latency >= 0) {
1242		dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
1243		if (dma_latency_fd < 0) {
1244			err_msg("Could not set /dev/cpu_dma_latency.\n");
1245			goto out_free;
1246		}
1247	}
1248
1249	if (params->deepest_idle_state >= -1) {
1250		if (!have_libcpupower_support()) {
1251			err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n");
1252			goto out_free;
1253		}
1254
1255		nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
1256
1257		for (i = 0; i < nr_cpus; i++) {
1258			if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
1259				continue;
1260			if (save_cpu_idle_disable_state(i) < 0) {
1261				err_msg("Could not save cpu idle state.\n");
1262				goto out_free;
1263			}
1264			if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) {
1265				err_msg("Could not set deepest cpu idle state.\n");
1266				goto out_free;
1267			}
1268		}
1269	}
1270
1271	if (params->trace_output) {
1272		record = osnoise_init_trace_tool("timerlat");
1273		if (!record) {
1274			err_msg("Failed to enable the trace instance\n");
1275			goto out_free;
1276		}
1277
1278		if (params->events) {
1279			retval = trace_events_enable(&record->trace, params->events);
1280			if (retval)
1281				goto out_hist;
1282		}
1283
1284		if (params->buffer_size > 0) {
1285			retval = trace_set_buffer_size(&record->trace, params->buffer_size);
1286			if (retval)
1287				goto out_hist;
1288		}
1289	}
1290
1291	if (!params->no_aa) {
1292		aa = osnoise_init_tool("timerlat_aa");
1293		if (!aa)
1294			goto out_hist;
1295
1296		retval = timerlat_aa_init(aa, params->dump_tasks);
1297		if (retval) {
1298			err_msg("Failed to enable the auto analysis instance\n");
1299			goto out_hist;
1300		}
1301
1302		retval = enable_timerlat(&aa->trace);
1303		if (retval) {
1304			err_msg("Failed to enable timerlat tracer\n");
1305			goto out_hist;
1306		}
1307	}
1308
1309	if (params->user_workload) {
1310		/* rtla asked to stop */
1311		params_u.should_run = 1;
1312		/* all threads left */
1313		params_u.stopped_running = 0;
1314
1315		params_u.set = &params->monitored_cpus;
1316		if (params->set_sched)
1317			params_u.sched_param = &params->sched_param;
1318		else
1319			params_u.sched_param = NULL;
1320
1321		params_u.cgroup_name = params->cgroup_name;
1322
1323		retval = pthread_create(&timerlat_u, NULL, timerlat_u_dispatcher, &params_u);
1324		if (retval)
1325			err_msg("Error creating timerlat user-space threads\n");
1326	}
1327
1328	if (params->warmup > 0) {
1329		debug_msg("Warming up for %d seconds\n", params->warmup);
1330		sleep(params->warmup);
1331		if (stop_tracing)
1332			goto out_hist;
1333	}
1334
1335	/*
1336	 * Start the tracers here, after having set all instances.
1337	 *
1338	 * Let the trace instance start first for the case of hitting a stop
1339	 * tracing while enabling other instances. The trace instance is the
1340	 * one with most valuable information.
1341	 */
1342	if (params->trace_output)
1343		trace_instance_start(&record->trace);
1344	if (!params->no_aa)
1345		trace_instance_start(&aa->trace);
1346	trace_instance_start(trace);
1347
1348	tool->start_time = time(NULL);
1349	timerlat_hist_set_signals(params);
1350
1351	while (!stop_tracing) {
1352		sleep(params->sleep_time);
1353
1354		retval = tracefs_iterate_raw_events(trace->tep,
1355						    trace->inst,
1356						    NULL,
1357						    0,
1358						    collect_registered_events,
1359						    trace);
1360		if (retval < 0) {
1361			err_msg("Error iterating on events\n");
1362			goto out_hist;
1363		}
1364
1365		if (trace_is_off(&tool->trace, &record->trace))
1366			break;
1367
1368		/* is there still any user-threads ? */
1369		if (params->user_workload) {
1370			if (params_u.stopped_running) {
1371				debug_msg("timerlat user-space threads stopped!\n");
1372				break;
1373			}
1374		}
1375	}
1376
1377	if (params->user_workload && !params_u.stopped_running) {
1378		params_u.should_run = 0;
1379		sleep(1);
1380	}
1381
1382	timerlat_print_stats(params, tool);
1383
1384	return_value = 0;
1385
1386	if (trace_is_off(&tool->trace, &record->trace) && !stop_tracing) {
1387		printf("rtla timerlat hit stop tracing\n");
1388
1389		if (!params->no_aa)
1390			timerlat_auto_analysis(params->stop_us, params->stop_total_us);
1391
1392		if (params->trace_output) {
1393			printf("  Saving trace to %s\n", params->trace_output);
1394			save_trace_to_file(record->trace.inst, params->trace_output);
1395		}
1396	}
1397
1398out_hist:
1399	timerlat_aa_destroy();
1400	if (dma_latency_fd >= 0)
1401		close(dma_latency_fd);
1402	if (params->deepest_idle_state >= -1) {
1403		for (i = 0; i < nr_cpus; i++) {
1404			if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
1405				continue;
1406			restore_cpu_idle_disable_state(i);
1407		}
1408	}
1409	trace_events_destroy(&record->trace, params->events);
1410	params->events = NULL;
1411out_free:
1412	timerlat_free_histogram(tool->data);
1413	osnoise_destroy_tool(aa);
1414	osnoise_destroy_tool(record);
1415	osnoise_destroy_tool(tool);
1416	free(params);
1417	free_cpu_idle_disable_states();
1418out_exit:
1419	exit(return_value);
1420}