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 
18 struct 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 	struct sched_attr	sched_param;
32 
33 	char			no_irq;
34 	char			no_thread;
35 	char			no_header;
36 	char			no_summary;
37 	char			no_index;
38 	char			with_zeros;
39 	int			bucket_size;
40 	int			entries;
41 };
42 
43 struct timerlat_hist_cpu {
44 	int			*irq;
45 	int			*thread;
46 
47 	int			irq_count;
48 	int			thread_count;
49 
50 	unsigned long long	min_irq;
51 	unsigned long long	sum_irq;
52 	unsigned long long	max_irq;
53 
54 	unsigned long long	min_thread;
55 	unsigned long long	sum_thread;
56 	unsigned long long	max_thread;
57 };
58 
59 struct timerlat_hist_data {
60 	struct timerlat_hist_cpu	*hist;
61 	int				entries;
62 	int				bucket_size;
63 	int				nr_cpus;
64 };
65 
66 /*
67  * timerlat_free_histogram - free runtime data
68  */
69 static void
70 timerlat_free_histogram(struct timerlat_hist_data *data)
71 {
72 	int cpu;
73 
74 	/* one histogram for IRQ and one for thread, per CPU */
75 	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
76 		if (data->hist[cpu].irq)
77 			free(data->hist[cpu].irq);
78 
79 		if (data->hist[cpu].thread)
80 			free(data->hist[cpu].thread);
81 	}
82 
83 	/* one set of histograms per CPU */
84 	if (data->hist)
85 		free(data->hist);
86 
87 	free(data);
88 }
89 
90 /*
91  * timerlat_alloc_histogram - alloc runtime data
92  */
93 static struct timerlat_hist_data
94 *timerlat_alloc_histogram(int nr_cpus, int entries, int bucket_size)
95 {
96 	struct timerlat_hist_data *data;
97 	int cpu;
98 
99 	data = calloc(1, sizeof(*data));
100 	if (!data)
101 		return NULL;
102 
103 	data->entries = entries;
104 	data->bucket_size = bucket_size;
105 	data->nr_cpus = nr_cpus;
106 
107 	/* one set of histograms per CPU */
108 	data->hist = calloc(1, sizeof(*data->hist) * nr_cpus);
109 	if (!data->hist)
110 		goto cleanup;
111 
112 	/* one histogram for IRQ and one for thread, per cpu */
113 	for (cpu = 0; cpu < nr_cpus; cpu++) {
114 		data->hist[cpu].irq = calloc(1, sizeof(*data->hist->irq) * (entries + 1));
115 		if (!data->hist[cpu].irq)
116 			goto cleanup;
117 		data->hist[cpu].thread = calloc(1, sizeof(*data->hist->thread) * (entries + 1));
118 		if (!data->hist[cpu].thread)
119 			goto cleanup;
120 	}
121 
122 	/* set the min to max */
123 	for (cpu = 0; cpu < nr_cpus; cpu++) {
124 		data->hist[cpu].min_irq = ~0;
125 		data->hist[cpu].min_thread = ~0;
126 	}
127 
128 	return data;
129 
130 cleanup:
131 	timerlat_free_histogram(data);
132 	return NULL;
133 }
134 
135 /*
136  * timerlat_hist_update - record a new timerlat occurent on cpu, updating data
137  */
138 static void
139 timerlat_hist_update(struct osnoise_tool *tool, int cpu,
140 		     unsigned long long thread,
141 		     unsigned long long latency)
142 {
143 	struct timerlat_hist_params *params = tool->params;
144 	struct timerlat_hist_data *data = tool->data;
145 	int entries = data->entries;
146 	int bucket;
147 	int *hist;
148 
149 	if (params->output_divisor)
150 		latency = latency / params->output_divisor;
151 
152 	if (data->bucket_size)
153 		bucket = latency / data->bucket_size;
154 
155 	if (!thread) {
156 		hist = data->hist[cpu].irq;
157 		data->hist[cpu].irq_count++;
158 		update_min(&data->hist[cpu].min_irq, &latency);
159 		update_sum(&data->hist[cpu].sum_irq, &latency);
160 		update_max(&data->hist[cpu].max_irq, &latency);
161 	} else {
162 		hist = data->hist[cpu].thread;
163 		data->hist[cpu].thread_count++;
164 		update_min(&data->hist[cpu].min_thread, &latency);
165 		update_sum(&data->hist[cpu].sum_thread, &latency);
166 		update_max(&data->hist[cpu].max_thread, &latency);
167 	}
168 
169 	if (bucket < entries)
170 		hist[bucket]++;
171 	else
172 		hist[entries]++;
173 }
174 
175 /*
176  * timerlat_hist_handler - this is the handler for timerlat tracer events
177  */
178 static int
179 timerlat_hist_handler(struct trace_seq *s, struct tep_record *record,
180 		     struct tep_event *event, void *data)
181 {
182 	struct trace_instance *trace = data;
183 	unsigned long long thread, latency;
184 	struct osnoise_tool *tool;
185 	int cpu = record->cpu;
186 
187 	tool = container_of(trace, struct osnoise_tool, trace);
188 
189 	tep_get_field_val(s, event, "context", record, &thread, 1);
190 	tep_get_field_val(s, event, "timer_latency", record, &latency, 1);
191 
192 	timerlat_hist_update(tool, cpu, thread, latency);
193 
194 	return 0;
195 }
196 
197 /*
198  * timerlat_hist_header - print the header of the tracer to the output
199  */
200 static void timerlat_hist_header(struct osnoise_tool *tool)
201 {
202 	struct timerlat_hist_params *params = tool->params;
203 	struct timerlat_hist_data *data = tool->data;
204 	struct trace_seq *s = tool->trace.seq;
205 	char duration[26];
206 	int cpu;
207 
208 	if (params->no_header)
209 		return;
210 
211 	get_duration(tool->start_time, duration, sizeof(duration));
212 	trace_seq_printf(s, "# RTLA timerlat histogram\n");
213 	trace_seq_printf(s, "# Time unit is %s (%s)\n",
214 			params->output_divisor == 1 ? "nanoseconds" : "microseconds",
215 			params->output_divisor == 1 ? "ns" : "us");
216 
217 	trace_seq_printf(s, "# Duration: %s\n", duration);
218 
219 	if (!params->no_index)
220 		trace_seq_printf(s, "Index");
221 
222 	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
223 		if (params->cpus && !params->monitored_cpus[cpu])
224 			continue;
225 
226 		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
227 			continue;
228 
229 		if (!params->no_irq)
230 			trace_seq_printf(s, "   IRQ-%03d", cpu);
231 
232 		if (!params->no_thread)
233 			trace_seq_printf(s, "   Thr-%03d", cpu);
234 	}
235 	trace_seq_printf(s, "\n");
236 
237 
238 	trace_seq_do_printf(s);
239 	trace_seq_reset(s);
240 }
241 
242 /*
243  * timerlat_print_summary - print the summary of the hist data to the output
244  */
245 static void
246 timerlat_print_summary(struct timerlat_hist_params *params,
247 		       struct trace_instance *trace,
248 		       struct timerlat_hist_data *data)
249 {
250 	int cpu;
251 
252 	if (params->no_summary)
253 		return;
254 
255 	if (!params->no_index)
256 		trace_seq_printf(trace->seq, "count:");
257 
258 	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
259 		if (params->cpus && !params->monitored_cpus[cpu])
260 			continue;
261 
262 		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
263 			continue;
264 
265 		if (!params->no_irq)
266 			trace_seq_printf(trace->seq, "%9d ",
267 					data->hist[cpu].irq_count);
268 
269 		if (!params->no_thread)
270 			trace_seq_printf(trace->seq, "%9d ",
271 					data->hist[cpu].thread_count);
272 	}
273 	trace_seq_printf(trace->seq, "\n");
274 
275 	if (!params->no_index)
276 		trace_seq_printf(trace->seq, "min:  ");
277 
278 	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
279 		if (params->cpus && !params->monitored_cpus[cpu])
280 			continue;
281 
282 		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
283 			continue;
284 
285 		if (!params->no_irq)
286 			trace_seq_printf(trace->seq, "%9llu ",
287 					data->hist[cpu].min_irq);
288 
289 		if (!params->no_thread)
290 			trace_seq_printf(trace->seq, "%9llu ",
291 					data->hist[cpu].min_thread);
292 	}
293 	trace_seq_printf(trace->seq, "\n");
294 
295 	if (!params->no_index)
296 		trace_seq_printf(trace->seq, "avg:  ");
297 
298 	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
299 		if (params->cpus && !params->monitored_cpus[cpu])
300 			continue;
301 
302 		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
303 			continue;
304 
305 		if (!params->no_irq) {
306 			if (data->hist[cpu].irq_count)
307 				trace_seq_printf(trace->seq, "%9llu ",
308 						 data->hist[cpu].sum_irq / data->hist[cpu].irq_count);
309 			else
310 				trace_seq_printf(trace->seq, "        - ");
311 		}
312 
313 		if (!params->no_thread) {
314 			if (data->hist[cpu].thread_count)
315 				trace_seq_printf(trace->seq, "%9llu ",
316 						data->hist[cpu].sum_thread / data->hist[cpu].thread_count);
317 			else
318 				trace_seq_printf(trace->seq, "        - ");
319 		}
320 	}
321 	trace_seq_printf(trace->seq, "\n");
322 
323 	if (!params->no_index)
324 		trace_seq_printf(trace->seq, "max:  ");
325 
326 	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
327 		if (params->cpus && !params->monitored_cpus[cpu])
328 			continue;
329 
330 		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
331 			continue;
332 
333 		if (!params->no_irq)
334 			trace_seq_printf(trace->seq, "%9llu ",
335 					data->hist[cpu].max_irq);
336 
337 		if (!params->no_thread)
338 			trace_seq_printf(trace->seq, "%9llu ",
339 					data->hist[cpu].max_thread);
340 	}
341 	trace_seq_printf(trace->seq, "\n");
342 	trace_seq_do_printf(trace->seq);
343 	trace_seq_reset(trace->seq);
344 }
345 
346 /*
347  * timerlat_print_stats - print data for all CPUs
348  */
349 static void
350 timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *tool)
351 {
352 	struct timerlat_hist_data *data = tool->data;
353 	struct trace_instance *trace = &tool->trace;
354 	int bucket, cpu;
355 	int total;
356 
357 	timerlat_hist_header(tool);
358 
359 	for (bucket = 0; bucket < data->entries; bucket++) {
360 		total = 0;
361 
362 		if (!params->no_index)
363 			trace_seq_printf(trace->seq, "%-6d",
364 					 bucket * data->bucket_size);
365 
366 		for (cpu = 0; cpu < data->nr_cpus; cpu++) {
367 			if (params->cpus && !params->monitored_cpus[cpu])
368 				continue;
369 
370 			if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
371 				continue;
372 
373 			if (!params->no_irq) {
374 				total += data->hist[cpu].irq[bucket];
375 				trace_seq_printf(trace->seq, "%9d ",
376 						data->hist[cpu].irq[bucket]);
377 			}
378 
379 			if (!params->no_thread) {
380 				total += data->hist[cpu].thread[bucket];
381 				trace_seq_printf(trace->seq, "%9d ",
382 						data->hist[cpu].thread[bucket]);
383 			}
384 
385 		}
386 
387 		if (total == 0 && !params->with_zeros) {
388 			trace_seq_reset(trace->seq);
389 			continue;
390 		}
391 
392 		trace_seq_printf(trace->seq, "\n");
393 		trace_seq_do_printf(trace->seq);
394 		trace_seq_reset(trace->seq);
395 	}
396 
397 	if (!params->no_index)
398 		trace_seq_printf(trace->seq, "over: ");
399 
400 	for (cpu = 0; cpu < data->nr_cpus; cpu++) {
401 		if (params->cpus && !params->monitored_cpus[cpu])
402 			continue;
403 
404 		if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
405 			continue;
406 
407 		if (!params->no_irq)
408 			trace_seq_printf(trace->seq, "%9d ",
409 					 data->hist[cpu].irq[data->entries]);
410 
411 		if (!params->no_thread)
412 			trace_seq_printf(trace->seq, "%9d ",
413 					 data->hist[cpu].thread[data->entries]);
414 	}
415 	trace_seq_printf(trace->seq, "\n");
416 	trace_seq_do_printf(trace->seq);
417 	trace_seq_reset(trace->seq);
418 
419 	timerlat_print_summary(params, trace, data);
420 }
421 
422 /*
423  * timerlat_hist_usage - prints timerlat top usage message
424  */
425 static void timerlat_hist_usage(char *usage)
426 {
427 	int i;
428 
429 	char *msg[] = {
430 		"",
431 		"  usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] [-t[=file]] \\",
432 		"         [-c cpu-list] [-P priority] [-e N] [-b N]  [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",
433 		"         [--no-index] [--with-zeros]",
434 		"",
435 		"	  -h/--help: print this menu",
436 		"	  -p/--period us: timerlat period in us",
437 		"	  -i/--irq us: stop trace if the irq latency is higher than the argument in us",
438 		"	  -T/--thread us: stop trace if the thread latency is higher than the argument in us",
439 		"	  -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us",
440 		"	  -c/--cpus cpus: run the tracer only on the given cpus",
441 		"	  -d/--duration time[m|h|d]: duration of the session in seconds",
442 		"	  -D/--debug: print debug info",
443 		"	  -T/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]",
444 		"	  -n/--nano: display data in nanoseconds",
445 		"	  -b/--bucket-size N: set the histogram bucket size (default 1)",
446 		"	  -e/--entries N: set the number of entries of the histogram (default 256)",
447 		"	     --no-irq: ignore IRQ latencies",
448 		"	     --no-thread: ignore thread latencies",
449 		"	     --no-header: do not print header",
450 		"	     --no-summary: do not print summary",
451 		"	     --no-index: do not print index",
452 		"	     --with-zeros: print zero only entries",
453 		"	  -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
454 		"		o:prio - use SCHED_OTHER with prio",
455 		"		r:prio - use SCHED_RR with prio",
456 		"		f:prio - use SCHED_FIFO with prio",
457 		"		d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
458 		"						       in nanoseconds",
459 		NULL,
460 	};
461 
462 	if (usage)
463 		fprintf(stderr, "%s\n", usage);
464 
465 	fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n",
466 			VERSION);
467 
468 	for (i = 0; msg[i]; i++)
469 		fprintf(stderr, "%s\n", msg[i]);
470 	exit(1);
471 }
472 
473 /*
474  * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters
475  */
476 static struct timerlat_hist_params
477 *timerlat_hist_parse_args(int argc, char *argv[])
478 {
479 	struct timerlat_hist_params *params;
480 	int retval;
481 	int c;
482 
483 	params = calloc(1, sizeof(*params));
484 	if (!params)
485 		exit(1);
486 
487 	/* display data in microseconds */
488 	params->output_divisor = 1000;
489 	params->bucket_size = 1;
490 	params->entries = 256;
491 
492 	while (1) {
493 		static struct option long_options[] = {
494 			{"cpus",		required_argument,	0, 'c'},
495 			{"bucket-size",		required_argument,	0, 'b'},
496 			{"debug",		no_argument,		0, 'D'},
497 			{"entries",		required_argument,	0, 'e'},
498 			{"duration",		required_argument,	0, 'd'},
499 			{"help",		no_argument,		0, 'h'},
500 			{"irq",			required_argument,	0, 'i'},
501 			{"nano",		no_argument,		0, 'n'},
502 			{"period",		required_argument,	0, 'p'},
503 			{"priority",		required_argument,	0, 'P'},
504 			{"stack",		required_argument,	0, 's'},
505 			{"thread",		required_argument,	0, 'T'},
506 			{"trace",		optional_argument,	0, 't'},
507 			{"no-irq",		no_argument,		0, '0'},
508 			{"no-thread",		no_argument,		0, '1'},
509 			{"no-header",		no_argument,		0, '2'},
510 			{"no-summary",		no_argument,		0, '3'},
511 			{"no-index",		no_argument,		0, '4'},
512 			{"with-zeros",		no_argument,		0, '5'},
513 			{0, 0, 0, 0}
514 		};
515 
516 		/* getopt_long stores the option index here. */
517 		int option_index = 0;
518 
519 		c = getopt_long(argc, argv, "c:b:d:e:Dhi:np:P:s:t::T:012345",
520 				 long_options, &option_index);
521 
522 		/* detect the end of the options. */
523 		if (c == -1)
524 			break;
525 
526 		switch (c) {
527 		case 'c':
528 			retval = parse_cpu_list(optarg, &params->monitored_cpus);
529 			if (retval)
530 				timerlat_hist_usage("\nInvalid -c cpu list\n");
531 			params->cpus = optarg;
532 			break;
533 		case 'b':
534 			params->bucket_size = get_llong_from_str(optarg);
535 			if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))
536 				timerlat_hist_usage("Bucket size needs to be > 0 and <= 1000000\n");
537 			break;
538 		case 'D':
539 			config_debug = 1;
540 			break;
541 		case 'd':
542 			params->duration = parse_seconds_duration(optarg);
543 			if (!params->duration)
544 				timerlat_hist_usage("Invalid -D duration\n");
545 			break;
546 		case 'e':
547 			params->entries = get_llong_from_str(optarg);
548 			if ((params->entries < 10) || (params->entries > 9999999))
549 					timerlat_hist_usage("Entries must be > 10 and < 9999999\n");
550 			break;
551 		case 'h':
552 		case '?':
553 			timerlat_hist_usage(NULL);
554 			break;
555 		case 'i':
556 			params->stop_us = get_llong_from_str(optarg);
557 			break;
558 		case 'n':
559 			params->output_divisor = 1;
560 			break;
561 		case 'p':
562 			params->timerlat_period_us = get_llong_from_str(optarg);
563 			if (params->timerlat_period_us > 1000000)
564 				timerlat_hist_usage("Period longer than 1 s\n");
565 			break;
566 		case 'P':
567 			retval = parse_prio(optarg, &params->sched_param);
568 			if (retval == -1)
569 				timerlat_hist_usage("Invalid -P priority");
570 			params->set_sched = 1;
571 			break;
572 		case 's':
573 			params->print_stack = get_llong_from_str(optarg);
574 			break;
575 		case 'T':
576 			params->stop_total_us = get_llong_from_str(optarg);
577 			break;
578 		case 't':
579 			if (optarg)
580 				/* skip = */
581 				params->trace_output = &optarg[1];
582 			else
583 				params->trace_output = "timerlat_trace.txt";
584 			break;
585 		case '0': /* no irq */
586 			params->no_irq = 1;
587 			break;
588 		case '1': /* no thread */
589 			params->no_thread = 1;
590 			break;
591 		case '2': /* no header */
592 			params->no_header = 1;
593 			break;
594 		case '3': /* no summary */
595 			params->no_summary = 1;
596 			break;
597 		case '4': /* no index */
598 			params->no_index = 1;
599 			break;
600 		case '5': /* with zeros */
601 			params->with_zeros = 1;
602 			break;
603 		default:
604 			timerlat_hist_usage("Invalid option");
605 		}
606 	}
607 
608 	if (geteuid()) {
609 		err_msg("rtla needs root permission\n");
610 		exit(EXIT_FAILURE);
611 	}
612 
613 	if (params->no_irq && params->no_thread)
614 		timerlat_hist_usage("no-irq and no-thread set, there is nothing to do here");
615 
616 	if (params->no_index && !params->with_zeros)
617 		timerlat_hist_usage("no-index set with with-zeros is not set - it does not make sense");
618 
619 	return params;
620 }
621 
622 /*
623  * timerlat_hist_apply_config - apply the hist configs to the initialized tool
624  */
625 static int
626 timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_hist_params *params)
627 {
628 	int retval;
629 
630 	if (!params->sleep_time)
631 		params->sleep_time = 1;
632 
633 	if (params->cpus) {
634 		retval = osnoise_set_cpus(tool->context, params->cpus);
635 		if (retval) {
636 			err_msg("Failed to apply CPUs config\n");
637 			goto out_err;
638 		}
639 	}
640 
641 	if (params->stop_us) {
642 		retval = osnoise_set_stop_us(tool->context, params->stop_us);
643 		if (retval) {
644 			err_msg("Failed to set stop us\n");
645 			goto out_err;
646 		}
647 	}
648 
649 	if (params->stop_total_us) {
650 		retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
651 		if (retval) {
652 			err_msg("Failed to set stop total us\n");
653 			goto out_err;
654 		}
655 	}
656 
657 	if (params->timerlat_period_us) {
658 		retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us);
659 		if (retval) {
660 			err_msg("Failed to set timerlat period\n");
661 			goto out_err;
662 		}
663 	}
664 
665 	if (params->print_stack) {
666 		retval = osnoise_set_print_stack(tool->context, params->print_stack);
667 		if (retval) {
668 			err_msg("Failed to set print stack\n");
669 			goto out_err;
670 		}
671 	}
672 
673 	return 0;
674 
675 out_err:
676 	return -1;
677 }
678 
679 /*
680  * timerlat_init_hist - initialize a timerlat hist tool with parameters
681  */
682 static struct osnoise_tool
683 *timerlat_init_hist(struct timerlat_hist_params *params)
684 {
685 	struct osnoise_tool *tool;
686 	int nr_cpus;
687 
688 	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
689 
690 	tool = osnoise_init_tool("timerlat_hist");
691 	if (!tool)
692 		return NULL;
693 
694 	tool->data = timerlat_alloc_histogram(nr_cpus, params->entries, params->bucket_size);
695 	if (!tool->data)
696 		goto out_err;
697 
698 	tool->params = params;
699 
700 	tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat",
701 				   timerlat_hist_handler, tool);
702 
703 	return tool;
704 
705 out_err:
706 	osnoise_destroy_tool(tool);
707 	return NULL;
708 }
709 
710 static int stop_tracing;
711 static void stop_hist(int sig)
712 {
713 	stop_tracing = 1;
714 }
715 
716 /*
717  * timerlat_hist_set_signals - handles the signal to stop the tool
718  */
719 static void
720 timerlat_hist_set_signals(struct timerlat_hist_params *params)
721 {
722 	signal(SIGINT, stop_hist);
723 	if (params->duration) {
724 		signal(SIGALRM, stop_hist);
725 		alarm(params->duration);
726 	}
727 }
728 
729 int timerlat_hist_main(int argc, char *argv[])
730 {
731 	struct timerlat_hist_params *params;
732 	struct trace_instance *trace;
733 	struct osnoise_tool *record;
734 	struct osnoise_tool *tool;
735 	int return_value = 1;
736 	int retval;
737 
738 	params = timerlat_hist_parse_args(argc, argv);
739 	if (!params)
740 		exit(1);
741 
742 	tool = timerlat_init_hist(params);
743 	if (!tool) {
744 		err_msg("Could not init osnoise hist\n");
745 		goto out_exit;
746 	}
747 
748 	retval = timerlat_hist_apply_config(tool, params);
749 	if (retval) {
750 		err_msg("Could not apply config\n");
751 		goto out_hist;
752 	}
753 
754 	trace = &tool->trace;
755 
756 	retval = enable_timerlat(trace);
757 	if (retval) {
758 		err_msg("Failed to enable timerlat tracer\n");
759 		goto out_hist;
760 	}
761 
762 	if (params->set_sched) {
763 		retval = set_comm_sched_attr("timerlat/", &params->sched_param);
764 		if (retval) {
765 			err_msg("Failed to set sched parameters\n");
766 			goto out_hist;
767 		}
768 	}
769 
770 	trace_instance_start(trace);
771 
772 	if (params->trace_output) {
773 		record = osnoise_init_trace_tool("timerlat");
774 		if (!record) {
775 			err_msg("Failed to enable the trace instance\n");
776 			goto out_hist;
777 		}
778 		trace_instance_start(&record->trace);
779 	}
780 
781 	tool->start_time = time(NULL);
782 	timerlat_hist_set_signals(params);
783 
784 	while (!stop_tracing) {
785 		sleep(params->sleep_time);
786 
787 		retval = tracefs_iterate_raw_events(trace->tep,
788 						    trace->inst,
789 						    NULL,
790 						    0,
791 						    collect_registered_events,
792 						    trace);
793 		if (retval < 0) {
794 			err_msg("Error iterating on events\n");
795 			goto out_hist;
796 		}
797 
798 		if (!tracefs_trace_is_on(trace->inst))
799 			break;
800 	};
801 
802 	timerlat_print_stats(params, tool);
803 
804 	return_value = 0;
805 
806 	if (!tracefs_trace_is_on(trace->inst)) {
807 		printf("rtla timelat hit stop tracing\n");
808 		if (params->trace_output) {
809 			printf("  Saving trace to %s\n", params->trace_output);
810 			save_trace_to_file(record->trace.inst, params->trace_output);
811 		}
812 	}
813 
814 out_hist:
815 	timerlat_free_histogram(tool->data);
816 	osnoise_destroy_tool(tool);
817 	if (params->trace_output)
818 		osnoise_destroy_tool(record);
819 	free(params);
820 out_exit:
821 	exit(return_value);
822 }
823