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