blob: 235f9620ef3d6ccb4663f0ef2285d31a00a24cfa [file] [log] [blame]
Daniel Bristot de Oliveira1eeb6322021-12-10 19:11:26 +01001// 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 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
43struct 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
59struct 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 */
69static void
70timerlat_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 */
93static 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
130cleanup:
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 */
138static void
139timerlat_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 */
178static int
179timerlat_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 */
200static 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 */
245static void
246timerlat_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 */
349static void
350timerlat_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 */
425static 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 */
476static 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 */
625static int
626timerlat_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
675out_err:
676 return -1;
677}
678
679/*
680 * timerlat_init_hist - initialize a timerlat hist tool with parameters
681 */
682static 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
705out_err:
706 osnoise_destroy_tool(tool);
707 return NULL;
708}
709
710static int stop_tracing;
711static 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 */
719static void
720timerlat_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
729int 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
814out_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);
820out_exit:
821 exit(return_value);
822}