0001
0002
0003
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
0070
0071 static void
0072 timerlat_free_histogram(struct timerlat_hist_data *data)
0073 {
0074 int cpu;
0075
0076
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
0086 if (data->hist)
0087 free(data->hist);
0088
0089 free(data);
0090 }
0091
0092
0093
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
0110 data->hist = calloc(1, sizeof(*data->hist) * nr_cpus);
0111 if (!data->hist)
0112 goto cleanup;
0113
0114
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
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
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
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
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
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
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
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
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
0498 params->dma_latency = -1;
0499
0500
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
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
0541 if (c == -1)
0542 break;
0543
0544 switch (c) {
0545 case 'a':
0546 auto_thresh = get_llong_from_str(optarg);
0547
0548
0549 params->stop_total_us = auto_thresh;
0550
0551
0552 params->print_stack = auto_thresh;
0553
0554
0555 params->trace_output = "timerlat_trace.txt";
0556
0557 break;
0558 case 'c':
0559 retval = parse_cpu_list(optarg, ¶ms->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, ¶ms->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
0624 params->trace_output = &optarg[1];
0625 else
0626 params->trace_output = "timerlat_trace.txt";
0627 break;
0628 case '0':
0629 params->no_irq = 1;
0630 break;
0631 case '1':
0632 params->no_thread = 1;
0633 break;
0634 case '2':
0635 params->no_header = 1;
0636 break;
0637 case '3':
0638 params->no_summary = 1;
0639 break;
0640 case '4':
0641 params->no_index = 1;
0642 break;
0643 case '5':
0644 params->with_zeros = 1;
0645 break;
0646 case '6':
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':
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
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
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
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/", ¶ms->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 }