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