Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
0004  */
0005 
0006 #include <getopt.h>
0007 #include <stdlib.h>
0008 #include <string.h>
0009 #include <signal.h>
0010 #include <unistd.h>
0011 #include <stdio.h>
0012 #include <time.h>
0013 
0014 #include "utils.h"
0015 #include "osnoise.h"
0016 #include "timerlat.h"
0017 
0018 struct timerlat_hist_params {
0019     char            *cpus;
0020     char            *monitored_cpus;
0021     char            *trace_output;
0022     unsigned long long  runtime;
0023     long long       stop_us;
0024     long long       stop_total_us;
0025     long long       timerlat_period_us;
0026     long long       print_stack;
0027     int         sleep_time;
0028     int         output_divisor;
0029     int         duration;
0030     int         set_sched;
0031     int         dma_latency;
0032     struct sched_attr   sched_param;
0033     struct trace_events *events;
0034 
0035     char            no_irq;
0036     char            no_thread;
0037     char            no_header;
0038     char            no_summary;
0039     char            no_index;
0040     char            with_zeros;
0041     int         bucket_size;
0042     int         entries;
0043 };
0044 
0045 struct timerlat_hist_cpu {
0046     int         *irq;
0047     int         *thread;
0048 
0049     int         irq_count;
0050     int         thread_count;
0051 
0052     unsigned long long  min_irq;
0053     unsigned long long  sum_irq;
0054     unsigned long long  max_irq;
0055 
0056     unsigned long long  min_thread;
0057     unsigned long long  sum_thread;
0058     unsigned long long  max_thread;
0059 };
0060 
0061 struct timerlat_hist_data {
0062     struct timerlat_hist_cpu    *hist;
0063     int             entries;
0064     int             bucket_size;
0065     int             nr_cpus;
0066 };
0067 
0068 /*
0069  * timerlat_free_histogram - free runtime data
0070  */
0071 static void
0072 timerlat_free_histogram(struct timerlat_hist_data *data)
0073 {
0074     int cpu;
0075 
0076     /* one histogram for IRQ and one for thread, per CPU */
0077     for (cpu = 0; cpu < data->nr_cpus; cpu++) {
0078         if (data->hist[cpu].irq)
0079             free(data->hist[cpu].irq);
0080 
0081         if (data->hist[cpu].thread)
0082             free(data->hist[cpu].thread);
0083     }
0084 
0085     /* one set of histograms per CPU */
0086     if (data->hist)
0087         free(data->hist);
0088 
0089     free(data);
0090 }
0091 
0092 /*
0093  * timerlat_alloc_histogram - alloc runtime data
0094  */
0095 static struct timerlat_hist_data
0096 *timerlat_alloc_histogram(int nr_cpus, int entries, int bucket_size)
0097 {
0098     struct timerlat_hist_data *data;
0099     int cpu;
0100 
0101     data = calloc(1, sizeof(*data));
0102     if (!data)
0103         return NULL;
0104 
0105     data->entries = entries;
0106     data->bucket_size = bucket_size;
0107     data->nr_cpus = nr_cpus;
0108 
0109     /* one set of histograms per CPU */
0110     data->hist = calloc(1, sizeof(*data->hist) * nr_cpus);
0111     if (!data->hist)
0112         goto cleanup;
0113 
0114     /* one histogram for IRQ and one for thread, per cpu */
0115     for (cpu = 0; cpu < nr_cpus; cpu++) {
0116         data->hist[cpu].irq = calloc(1, sizeof(*data->hist->irq) * (entries + 1));
0117         if (!data->hist[cpu].irq)
0118             goto cleanup;
0119         data->hist[cpu].thread = calloc(1, sizeof(*data->hist->thread) * (entries + 1));
0120         if (!data->hist[cpu].thread)
0121             goto cleanup;
0122     }
0123 
0124     /* set the min to max */
0125     for (cpu = 0; cpu < nr_cpus; cpu++) {
0126         data->hist[cpu].min_irq = ~0;
0127         data->hist[cpu].min_thread = ~0;
0128     }
0129 
0130     return data;
0131 
0132 cleanup:
0133     timerlat_free_histogram(data);
0134     return NULL;
0135 }
0136 
0137 /*
0138  * timerlat_hist_update - record a new timerlat occurent on cpu, updating data
0139  */
0140 static void
0141 timerlat_hist_update(struct osnoise_tool *tool, int cpu,
0142              unsigned long long thread,
0143              unsigned long long latency)
0144 {
0145     struct timerlat_hist_params *params = tool->params;
0146     struct timerlat_hist_data *data = tool->data;
0147     int entries = data->entries;
0148     int bucket;
0149     int *hist;
0150 
0151     if (params->output_divisor)
0152         latency = latency / params->output_divisor;
0153 
0154     if (data->bucket_size)
0155         bucket = latency / data->bucket_size;
0156 
0157     if (!thread) {
0158         hist = data->hist[cpu].irq;
0159         data->hist[cpu].irq_count++;
0160         update_min(&data->hist[cpu].min_irq, &latency);
0161         update_sum(&data->hist[cpu].sum_irq, &latency);
0162         update_max(&data->hist[cpu].max_irq, &latency);
0163     } else {
0164         hist = data->hist[cpu].thread;
0165         data->hist[cpu].thread_count++;
0166         update_min(&data->hist[cpu].min_thread, &latency);
0167         update_sum(&data->hist[cpu].sum_thread, &latency);
0168         update_max(&data->hist[cpu].max_thread, &latency);
0169     }
0170 
0171     if (bucket < entries)
0172         hist[bucket]++;
0173     else
0174         hist[entries]++;
0175 }
0176 
0177 /*
0178  * timerlat_hist_handler - this is the handler for timerlat tracer events
0179  */
0180 static int
0181 timerlat_hist_handler(struct trace_seq *s, struct tep_record *record,
0182              struct tep_event *event, void *data)
0183 {
0184     struct trace_instance *trace = data;
0185     unsigned long long thread, latency;
0186     struct osnoise_tool *tool;
0187     int cpu = record->cpu;
0188 
0189     tool = container_of(trace, struct osnoise_tool, trace);
0190 
0191     tep_get_field_val(s, event, "context", record, &thread, 1);
0192     tep_get_field_val(s, event, "timer_latency", record, &latency, 1);
0193 
0194     timerlat_hist_update(tool, cpu, thread, latency);
0195 
0196     return 0;
0197 }
0198 
0199 /*
0200  * timerlat_hist_header - print the header of the tracer to the output
0201  */
0202 static void timerlat_hist_header(struct osnoise_tool *tool)
0203 {
0204     struct timerlat_hist_params *params = tool->params;
0205     struct timerlat_hist_data *data = tool->data;
0206     struct trace_seq *s = tool->trace.seq;
0207     char duration[26];
0208     int cpu;
0209 
0210     if (params->no_header)
0211         return;
0212 
0213     get_duration(tool->start_time, duration, sizeof(duration));
0214     trace_seq_printf(s, "# RTLA timerlat histogram\n");
0215     trace_seq_printf(s, "# Time unit is %s (%s)\n",
0216             params->output_divisor == 1 ? "nanoseconds" : "microseconds",
0217             params->output_divisor == 1 ? "ns" : "us");
0218 
0219     trace_seq_printf(s, "# Duration: %s\n", duration);
0220 
0221     if (!params->no_index)
0222         trace_seq_printf(s, "Index");
0223 
0224     for (cpu = 0; cpu < data->nr_cpus; cpu++) {
0225         if (params->cpus && !params->monitored_cpus[cpu])
0226             continue;
0227 
0228         if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
0229             continue;
0230 
0231         if (!params->no_irq)
0232             trace_seq_printf(s, "   IRQ-%03d", cpu);
0233 
0234         if (!params->no_thread)
0235             trace_seq_printf(s, "   Thr-%03d", cpu);
0236     }
0237     trace_seq_printf(s, "\n");
0238 
0239 
0240     trace_seq_do_printf(s);
0241     trace_seq_reset(s);
0242 }
0243 
0244 /*
0245  * timerlat_print_summary - print the summary of the hist data to the output
0246  */
0247 static void
0248 timerlat_print_summary(struct timerlat_hist_params *params,
0249                struct trace_instance *trace,
0250                struct timerlat_hist_data *data)
0251 {
0252     int cpu;
0253 
0254     if (params->no_summary)
0255         return;
0256 
0257     if (!params->no_index)
0258         trace_seq_printf(trace->seq, "count:");
0259 
0260     for (cpu = 0; cpu < data->nr_cpus; cpu++) {
0261         if (params->cpus && !params->monitored_cpus[cpu])
0262             continue;
0263 
0264         if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
0265             continue;
0266 
0267         if (!params->no_irq)
0268             trace_seq_printf(trace->seq, "%9d ",
0269                     data->hist[cpu].irq_count);
0270 
0271         if (!params->no_thread)
0272             trace_seq_printf(trace->seq, "%9d ",
0273                     data->hist[cpu].thread_count);
0274     }
0275     trace_seq_printf(trace->seq, "\n");
0276 
0277     if (!params->no_index)
0278         trace_seq_printf(trace->seq, "min:  ");
0279 
0280     for (cpu = 0; cpu < data->nr_cpus; cpu++) {
0281         if (params->cpus && !params->monitored_cpus[cpu])
0282             continue;
0283 
0284         if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
0285             continue;
0286 
0287         if (!params->no_irq)
0288             trace_seq_printf(trace->seq, "%9llu ",
0289                     data->hist[cpu].min_irq);
0290 
0291         if (!params->no_thread)
0292             trace_seq_printf(trace->seq, "%9llu ",
0293                     data->hist[cpu].min_thread);
0294     }
0295     trace_seq_printf(trace->seq, "\n");
0296 
0297     if (!params->no_index)
0298         trace_seq_printf(trace->seq, "avg:  ");
0299 
0300     for (cpu = 0; cpu < data->nr_cpus; cpu++) {
0301         if (params->cpus && !params->monitored_cpus[cpu])
0302             continue;
0303 
0304         if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
0305             continue;
0306 
0307         if (!params->no_irq) {
0308             if (data->hist[cpu].irq_count)
0309                 trace_seq_printf(trace->seq, "%9llu ",
0310                          data->hist[cpu].sum_irq / data->hist[cpu].irq_count);
0311             else
0312                 trace_seq_printf(trace->seq, "        - ");
0313         }
0314 
0315         if (!params->no_thread) {
0316             if (data->hist[cpu].thread_count)
0317                 trace_seq_printf(trace->seq, "%9llu ",
0318                         data->hist[cpu].sum_thread / data->hist[cpu].thread_count);
0319             else
0320                 trace_seq_printf(trace->seq, "        - ");
0321         }
0322     }
0323     trace_seq_printf(trace->seq, "\n");
0324 
0325     if (!params->no_index)
0326         trace_seq_printf(trace->seq, "max:  ");
0327 
0328     for (cpu = 0; cpu < data->nr_cpus; cpu++) {
0329         if (params->cpus && !params->monitored_cpus[cpu])
0330             continue;
0331 
0332         if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
0333             continue;
0334 
0335         if (!params->no_irq)
0336             trace_seq_printf(trace->seq, "%9llu ",
0337                     data->hist[cpu].max_irq);
0338 
0339         if (!params->no_thread)
0340             trace_seq_printf(trace->seq, "%9llu ",
0341                     data->hist[cpu].max_thread);
0342     }
0343     trace_seq_printf(trace->seq, "\n");
0344     trace_seq_do_printf(trace->seq);
0345     trace_seq_reset(trace->seq);
0346 }
0347 
0348 /*
0349  * timerlat_print_stats - print data for all CPUs
0350  */
0351 static void
0352 timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *tool)
0353 {
0354     struct timerlat_hist_data *data = tool->data;
0355     struct trace_instance *trace = &tool->trace;
0356     int bucket, cpu;
0357     int total;
0358 
0359     timerlat_hist_header(tool);
0360 
0361     for (bucket = 0; bucket < data->entries; bucket++) {
0362         total = 0;
0363 
0364         if (!params->no_index)
0365             trace_seq_printf(trace->seq, "%-6d",
0366                      bucket * data->bucket_size);
0367 
0368         for (cpu = 0; cpu < data->nr_cpus; cpu++) {
0369             if (params->cpus && !params->monitored_cpus[cpu])
0370                 continue;
0371 
0372             if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
0373                 continue;
0374 
0375             if (!params->no_irq) {
0376                 total += data->hist[cpu].irq[bucket];
0377                 trace_seq_printf(trace->seq, "%9d ",
0378                         data->hist[cpu].irq[bucket]);
0379             }
0380 
0381             if (!params->no_thread) {
0382                 total += data->hist[cpu].thread[bucket];
0383                 trace_seq_printf(trace->seq, "%9d ",
0384                         data->hist[cpu].thread[bucket]);
0385             }
0386 
0387         }
0388 
0389         if (total == 0 && !params->with_zeros) {
0390             trace_seq_reset(trace->seq);
0391             continue;
0392         }
0393 
0394         trace_seq_printf(trace->seq, "\n");
0395         trace_seq_do_printf(trace->seq);
0396         trace_seq_reset(trace->seq);
0397     }
0398 
0399     if (!params->no_index)
0400         trace_seq_printf(trace->seq, "over: ");
0401 
0402     for (cpu = 0; cpu < data->nr_cpus; cpu++) {
0403         if (params->cpus && !params->monitored_cpus[cpu])
0404             continue;
0405 
0406         if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count)
0407             continue;
0408 
0409         if (!params->no_irq)
0410             trace_seq_printf(trace->seq, "%9d ",
0411                      data->hist[cpu].irq[data->entries]);
0412 
0413         if (!params->no_thread)
0414             trace_seq_printf(trace->seq, "%9d ",
0415                      data->hist[cpu].thread[data->entries]);
0416     }
0417     trace_seq_printf(trace->seq, "\n");
0418     trace_seq_do_printf(trace->seq);
0419     trace_seq_reset(trace->seq);
0420 
0421     timerlat_print_summary(params, trace, data);
0422 }
0423 
0424 /*
0425  * timerlat_hist_usage - prints timerlat top usage message
0426  */
0427 static void timerlat_hist_usage(char *usage)
0428 {
0429     int i;
0430 
0431     char *msg[] = {
0432         "",
0433         "  usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\",
0434         "         [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] \\",
0435         "     [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",
0436         "     [--no-index] [--with-zeros] [--dma-latency us]",
0437         "",
0438         "     -h/--help: print this menu",
0439         "     -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
0440         "     -p/--period us: timerlat period in us",
0441         "     -i/--irq us: stop trace if the irq latency is higher than the argument in us",
0442         "     -T/--thread us: stop trace if the thread latency is higher than the argument in us",
0443         "     -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us",
0444         "     -c/--cpus cpus: run the tracer only on the given cpus",
0445         "     -d/--duration time[m|h|d]: duration of the session in seconds",
0446         "     -D/--debug: print debug info",
0447         "     -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]",
0448         "     -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
0449         "        --filter <filter>: enable a trace event filter to the previous -e event",
0450         "        --trigger <trigger>: enable a trace event trigger to the previous -e event",
0451         "     -n/--nano: display data in nanoseconds",
0452         "     -b/--bucket-size N: set the histogram bucket size (default 1)",
0453         "     -E/--entries N: set the number of entries of the histogram (default 256)",
0454         "        --no-irq: ignore IRQ latencies",
0455         "        --no-thread: ignore thread latencies",
0456         "        --no-header: do not print header",
0457         "        --no-summary: do not print summary",
0458         "        --no-index: do not print index",
0459         "        --with-zeros: print zero only entries",
0460         "        --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",
0461         "     -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
0462         "       o:prio - use SCHED_OTHER with prio",
0463         "       r:prio - use SCHED_RR with prio",
0464         "       f:prio - use SCHED_FIFO with prio",
0465         "       d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
0466         "                              in nanoseconds",
0467         NULL,
0468     };
0469 
0470     if (usage)
0471         fprintf(stderr, "%s\n", usage);
0472 
0473     fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n",
0474             VERSION);
0475 
0476     for (i = 0; msg[i]; i++)
0477         fprintf(stderr, "%s\n", msg[i]);
0478     exit(1);
0479 }
0480 
0481 /*
0482  * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters
0483  */
0484 static struct timerlat_hist_params
0485 *timerlat_hist_parse_args(int argc, char *argv[])
0486 {
0487     struct timerlat_hist_params *params;
0488     struct trace_events *tevent;
0489     int auto_thresh;
0490     int retval;
0491     int c;
0492 
0493     params = calloc(1, sizeof(*params));
0494     if (!params)
0495         exit(1);
0496 
0497     /* disabled by default */
0498     params->dma_latency = -1;
0499 
0500     /* display data in microseconds */
0501     params->output_divisor = 1000;
0502     params->bucket_size = 1;
0503     params->entries = 256;
0504 
0505     while (1) {
0506         static struct option long_options[] = {
0507             {"auto",        required_argument,  0, 'a'},
0508             {"cpus",        required_argument,  0, 'c'},
0509             {"bucket-size",     required_argument,  0, 'b'},
0510             {"debug",       no_argument,        0, 'D'},
0511             {"entries",     required_argument,  0, 'E'},
0512             {"duration",        required_argument,  0, 'd'},
0513             {"help",        no_argument,        0, 'h'},
0514             {"irq",         required_argument,  0, 'i'},
0515             {"nano",        no_argument,        0, 'n'},
0516             {"period",      required_argument,  0, 'p'},
0517             {"priority",        required_argument,  0, 'P'},
0518             {"stack",       required_argument,  0, 's'},
0519             {"thread",      required_argument,  0, 'T'},
0520             {"trace",       optional_argument,  0, 't'},
0521             {"event",       required_argument,  0, 'e'},
0522             {"no-irq",      no_argument,        0, '0'},
0523             {"no-thread",       no_argument,        0, '1'},
0524             {"no-header",       no_argument,        0, '2'},
0525             {"no-summary",      no_argument,        0, '3'},
0526             {"no-index",        no_argument,        0, '4'},
0527             {"with-zeros",      no_argument,        0, '5'},
0528             {"trigger",     required_argument,  0, '6'},
0529             {"filter",      required_argument,  0, '7'},
0530             {"dma-latency",     required_argument,  0, '8'},
0531             {0, 0, 0, 0}
0532         };
0533 
0534         /* getopt_long stores the option index here. */
0535         int option_index = 0;
0536 
0537         c = getopt_long(argc, argv, "a:c:b:d:e:E:Dhi:np:P:s:t::T:0123456:7:8:",
0538                  long_options, &option_index);
0539 
0540         /* detect the end of the options. */
0541         if (c == -1)
0542             break;
0543 
0544         switch (c) {
0545         case 'a':
0546             auto_thresh = get_llong_from_str(optarg);
0547 
0548             /* set thread stop to auto_thresh */
0549             params->stop_total_us = auto_thresh;
0550 
0551             /* get stack trace */
0552             params->print_stack = auto_thresh;
0553 
0554             /* set trace */
0555             params->trace_output = "timerlat_trace.txt";
0556 
0557             break;
0558         case 'c':
0559             retval = parse_cpu_list(optarg, &params->monitored_cpus);
0560             if (retval)
0561                 timerlat_hist_usage("\nInvalid -c cpu list\n");
0562             params->cpus = optarg;
0563             break;
0564         case 'b':
0565             params->bucket_size = get_llong_from_str(optarg);
0566             if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))
0567                 timerlat_hist_usage("Bucket size needs to be > 0 and <= 1000000\n");
0568             break;
0569         case 'D':
0570             config_debug = 1;
0571             break;
0572         case 'd':
0573             params->duration = parse_seconds_duration(optarg);
0574             if (!params->duration)
0575                 timerlat_hist_usage("Invalid -D duration\n");
0576             break;
0577         case 'e':
0578             tevent = trace_event_alloc(optarg);
0579             if (!tevent) {
0580                 err_msg("Error alloc trace event");
0581                 exit(EXIT_FAILURE);
0582             }
0583 
0584             if (params->events)
0585                 tevent->next = params->events;
0586 
0587             params->events = tevent;
0588             break;
0589         case 'E':
0590             params->entries = get_llong_from_str(optarg);
0591             if ((params->entries < 10) || (params->entries > 9999999))
0592                     timerlat_hist_usage("Entries must be > 10 and < 9999999\n");
0593             break;
0594         case 'h':
0595         case '?':
0596             timerlat_hist_usage(NULL);
0597             break;
0598         case 'i':
0599             params->stop_us = get_llong_from_str(optarg);
0600             break;
0601         case 'n':
0602             params->output_divisor = 1;
0603             break;
0604         case 'p':
0605             params->timerlat_period_us = get_llong_from_str(optarg);
0606             if (params->timerlat_period_us > 1000000)
0607                 timerlat_hist_usage("Period longer than 1 s\n");
0608             break;
0609         case 'P':
0610             retval = parse_prio(optarg, &params->sched_param);
0611             if (retval == -1)
0612                 timerlat_hist_usage("Invalid -P priority");
0613             params->set_sched = 1;
0614             break;
0615         case 's':
0616             params->print_stack = get_llong_from_str(optarg);
0617             break;
0618         case 'T':
0619             params->stop_total_us = get_llong_from_str(optarg);
0620             break;
0621         case 't':
0622             if (optarg)
0623                 /* skip = */
0624                 params->trace_output = &optarg[1];
0625             else
0626                 params->trace_output = "timerlat_trace.txt";
0627             break;
0628         case '0': /* no irq */
0629             params->no_irq = 1;
0630             break;
0631         case '1': /* no thread */
0632             params->no_thread = 1;
0633             break;
0634         case '2': /* no header */
0635             params->no_header = 1;
0636             break;
0637         case '3': /* no summary */
0638             params->no_summary = 1;
0639             break;
0640         case '4': /* no index */
0641             params->no_index = 1;
0642             break;
0643         case '5': /* with zeros */
0644             params->with_zeros = 1;
0645             break;
0646         case '6': /* trigger */
0647             if (params->events) {
0648                 retval = trace_event_add_trigger(params->events, optarg);
0649                 if (retval) {
0650                     err_msg("Error adding trigger %s\n", optarg);
0651                     exit(EXIT_FAILURE);
0652                 }
0653             } else {
0654                 timerlat_hist_usage("--trigger requires a previous -e\n");
0655             }
0656             break;
0657         case '7': /* filter */
0658             if (params->events) {
0659                 retval = trace_event_add_filter(params->events, optarg);
0660                 if (retval) {
0661                     err_msg("Error adding filter %s\n", optarg);
0662                     exit(EXIT_FAILURE);
0663                 }
0664             } else {
0665                 timerlat_hist_usage("--filter requires a previous -e\n");
0666             }
0667             break;
0668         case '8':
0669             params->dma_latency = get_llong_from_str(optarg);
0670             if (params->dma_latency < 0 || params->dma_latency > 10000) {
0671                 err_msg("--dma-latency needs to be >= 0 and < 10000");
0672                 exit(EXIT_FAILURE);
0673             }
0674             break;
0675         default:
0676             timerlat_hist_usage("Invalid option");
0677         }
0678     }
0679 
0680     if (geteuid()) {
0681         err_msg("rtla needs root permission\n");
0682         exit(EXIT_FAILURE);
0683     }
0684 
0685     if (params->no_irq && params->no_thread)
0686         timerlat_hist_usage("no-irq and no-thread set, there is nothing to do here");
0687 
0688     if (params->no_index && !params->with_zeros)
0689         timerlat_hist_usage("no-index set with with-zeros is not set - it does not make sense");
0690 
0691     return params;
0692 }
0693 
0694 /*
0695  * timerlat_hist_apply_config - apply the hist configs to the initialized tool
0696  */
0697 static int
0698 timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_hist_params *params)
0699 {
0700     int retval;
0701 
0702     if (!params->sleep_time)
0703         params->sleep_time = 1;
0704 
0705     if (params->cpus) {
0706         retval = osnoise_set_cpus(tool->context, params->cpus);
0707         if (retval) {
0708             err_msg("Failed to apply CPUs config\n");
0709             goto out_err;
0710         }
0711     }
0712 
0713     if (params->stop_us) {
0714         retval = osnoise_set_stop_us(tool->context, params->stop_us);
0715         if (retval) {
0716             err_msg("Failed to set stop us\n");
0717             goto out_err;
0718         }
0719     }
0720 
0721     if (params->stop_total_us) {
0722         retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
0723         if (retval) {
0724             err_msg("Failed to set stop total us\n");
0725             goto out_err;
0726         }
0727     }
0728 
0729     if (params->timerlat_period_us) {
0730         retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us);
0731         if (retval) {
0732             err_msg("Failed to set timerlat period\n");
0733             goto out_err;
0734         }
0735     }
0736 
0737     if (params->print_stack) {
0738         retval = osnoise_set_print_stack(tool->context, params->print_stack);
0739         if (retval) {
0740             err_msg("Failed to set print stack\n");
0741             goto out_err;
0742         }
0743     }
0744 
0745     return 0;
0746 
0747 out_err:
0748     return -1;
0749 }
0750 
0751 /*
0752  * timerlat_init_hist - initialize a timerlat hist tool with parameters
0753  */
0754 static struct osnoise_tool
0755 *timerlat_init_hist(struct timerlat_hist_params *params)
0756 {
0757     struct osnoise_tool *tool;
0758     int nr_cpus;
0759 
0760     nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
0761 
0762     tool = osnoise_init_tool("timerlat_hist");
0763     if (!tool)
0764         return NULL;
0765 
0766     tool->data = timerlat_alloc_histogram(nr_cpus, params->entries, params->bucket_size);
0767     if (!tool->data)
0768         goto out_err;
0769 
0770     tool->params = params;
0771 
0772     tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat",
0773                    timerlat_hist_handler, tool);
0774 
0775     return tool;
0776 
0777 out_err:
0778     osnoise_destroy_tool(tool);
0779     return NULL;
0780 }
0781 
0782 static int stop_tracing;
0783 static void stop_hist(int sig)
0784 {
0785     stop_tracing = 1;
0786 }
0787 
0788 /*
0789  * timerlat_hist_set_signals - handles the signal to stop the tool
0790  */
0791 static void
0792 timerlat_hist_set_signals(struct timerlat_hist_params *params)
0793 {
0794     signal(SIGINT, stop_hist);
0795     if (params->duration) {
0796         signal(SIGALRM, stop_hist);
0797         alarm(params->duration);
0798     }
0799 }
0800 
0801 int timerlat_hist_main(int argc, char *argv[])
0802 {
0803     struct timerlat_hist_params *params;
0804     struct osnoise_tool *record = NULL;
0805     struct osnoise_tool *tool = NULL;
0806     struct trace_instance *trace;
0807     int dma_latency_fd = -1;
0808     int return_value = 1;
0809     int retval;
0810 
0811     params = timerlat_hist_parse_args(argc, argv);
0812     if (!params)
0813         exit(1);
0814 
0815     tool = timerlat_init_hist(params);
0816     if (!tool) {
0817         err_msg("Could not init osnoise hist\n");
0818         goto out_exit;
0819     }
0820 
0821     retval = timerlat_hist_apply_config(tool, params);
0822     if (retval) {
0823         err_msg("Could not apply config\n");
0824         goto out_free;
0825     }
0826 
0827     trace = &tool->trace;
0828 
0829     retval = enable_timerlat(trace);
0830     if (retval) {
0831         err_msg("Failed to enable timerlat tracer\n");
0832         goto out_free;
0833     }
0834 
0835     if (params->set_sched) {
0836         retval = set_comm_sched_attr("timerlat/", &params->sched_param);
0837         if (retval) {
0838             err_msg("Failed to set sched parameters\n");
0839             goto out_free;
0840         }
0841     }
0842 
0843     if (params->dma_latency >= 0) {
0844         dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
0845         if (dma_latency_fd < 0) {
0846             err_msg("Could not set /dev/cpu_dma_latency.\n");
0847             goto out_free;
0848         }
0849     }
0850 
0851     trace_instance_start(trace);
0852 
0853     if (params->trace_output) {
0854         record = osnoise_init_trace_tool("timerlat");
0855         if (!record) {
0856             err_msg("Failed to enable the trace instance\n");
0857             goto out_free;
0858         }
0859 
0860         if (params->events) {
0861             retval = trace_events_enable(&record->trace, params->events);
0862             if (retval)
0863                 goto out_hist;
0864         }
0865 
0866         trace_instance_start(&record->trace);
0867     }
0868 
0869     tool->start_time = time(NULL);
0870     timerlat_hist_set_signals(params);
0871 
0872     while (!stop_tracing) {
0873         sleep(params->sleep_time);
0874 
0875         retval = tracefs_iterate_raw_events(trace->tep,
0876                             trace->inst,
0877                             NULL,
0878                             0,
0879                             collect_registered_events,
0880                             trace);
0881         if (retval < 0) {
0882             err_msg("Error iterating on events\n");
0883             goto out_hist;
0884         }
0885 
0886         if (trace_is_off(&tool->trace, &record->trace))
0887             break;
0888     }
0889 
0890     timerlat_print_stats(params, tool);
0891 
0892     return_value = 0;
0893 
0894     if (trace_is_off(&tool->trace, &record->trace)) {
0895         printf("rtla timerlat hit stop tracing\n");
0896         if (params->trace_output) {
0897             printf("  Saving trace to %s\n", params->trace_output);
0898             save_trace_to_file(record->trace.inst, params->trace_output);
0899         }
0900     }
0901 
0902 out_hist:
0903     if (dma_latency_fd >= 0)
0904         close(dma_latency_fd);
0905     trace_events_destroy(&record->trace, params->events);
0906     params->events = NULL;
0907 out_free:
0908     timerlat_free_histogram(tool->data);
0909     osnoise_destroy_tool(record);
0910     osnoise_destroy_tool(tool);
0911     free(params);
0912 out_exit:
0913     exit(return_value);
0914 }