History

Merge tag 'trace-rtla-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull RTLA tracing tool updates from Steven Rostedt: "Real Time Analysis Tool updatesfor 5.18: - Support for adjusting tracing_threashold - Add -a (auto) option to make it easier for users to debug in the field - Add -e option to add more events to the trace - Add --trigger option to add triggers to events - Add --filter option to filter events - Add support to save histograms to the file - Add --dma-latency to set /dev/cpu_dma_latency - Other fixes and cleanups" * tag 'trace-rtla-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: rtla: Tools main loop cleanup rtla/timerlat: Add --dma-latency option rtla/osnoise: Fix osnoise hist stop tracing message rtla: Check for trace off also in the trace instance rtla/trace: Save event histogram output to a file rtla: Add --filter support rtla/trace: Add trace event filter helpers rtla: Add --trigger support rtla/trace: Add trace event trigger helpers rtla: Add -e/--event support rtla/trace: Add trace events helpers rtla/timerlat: Add the automatic trace option rtla/osnoise: Add the automatic trace option rtla/osnoise: Add an option to set the threshold rtla/osnoise: Add support to adjust the tracing_thresh

Linus Torvalds committed 4 years ago

Commit 20f463f2 parents 3ef4ea3 75016ca13 files · +903 −28Browse files

Unified · Split

13 files
  1. Documentation/tools/rtla/common_options.rst +19 −0
  2. Documentation/tools/rtla/common_osnoise_options.rst +10 −0
  3. Documentation/tools/rtla/common_timerlat_options.rst +12 −0
  4. tools/tracing/rtla/src/osnoise.c +83 −0
  5. tools/tracing/rtla/src/osnoise.h +8 −0
  6. tools/tracing/rtla/src/osnoise_hist.c +87 −8
  7. tools/tracing/rtla/src/osnoise_top.c +85 −6
  8. tools/tracing/rtla/src/timerlat_hist.c +100 −8
  9. tools/tracing/rtla/src/timerlat_top.c +97 −6
  10. tools/tracing/rtla/src/trace.c +345 −0
  11. tools/tracing/rtla/src/trace.h +23 −0
  12. tools/tracing/rtla/src/utils.c +33 −0
  13. tools/tracing/rtla/src/utils.h +1 −0
Documentation/tools/rtla/common_options.rst+19−0
@@ -14,6 +14,25 @@ · 13 unmodified lines
14 14
15 Save the stopped trace to [*file|osnoise_trace.txt*].15 Save the stopped trace to [*file|osnoise_trace.txt*].
16 16
17**-e**, **--event** *sys:event*
18
19 Enable an event in the trace (**-t**) session. The argument can be a specific event, e.g., **-e** *sched:sched_switch*, or all events of a system group, e.g., **-e** *sched*. Multiple **-e** are allowed. It is only active when **-t** or **-a** are set.
20
21**--filter** *<filter>*
22
23 Filter the previous **-e** *sys:event* event with *<filter>*. For further information about event filtering see https://www.kernel.org/doc/html/latest/trace/events.html#event-filtering.
24
25**--trigger** *<trigger>*
26 Enable a trace event trigger to the previous **-e** *sys:event*.
27 If the *hist:* trigger is activated, the output histogram will be automatically saved to a file named *system_event_hist.txt*.
28 For example, the command:
29
30 rtla <command> <mode> -t -e osnoise:irq_noise --trigger="hist:key=desc,duration/1000:sort=desc,duration/1000:vals=hitcount"
31
32 Will automatically save the content of the histogram associated to *osnoise:irq_noise* event in *osnoise_irq_noise_hist.txt*.
33
34 For further information about event trigger see https://www.kernel.org/doc/html/latest/trace/events.html#event-triggers.
35
17**-P**, **--priority** *o:prio|r:prio|f:prio|d:runtime:period*36**-P**, **--priority** *o:prio|r:prio|f:prio|d:runtime:period*
18 37
19 Set scheduling parameters to the osnoise tracer threads, the format to set the priority are:38 Set scheduling parameters to the osnoise tracer threads, the format to set the priority are:
Documentation/tools/rtla/common_osnoise_options.rst+10−0
@@ -1,3 +1,8 @@
1**-a**, **--auto** *us*
2
3 Set the automatic trace mode. This mode sets some commonly used options
4 while debugging the system. It is equivalent to use **-s** *us* **-T 1 -t**.
5
1**-p**, **--period** *us*6**-p**, **--period** *us*
2 7
3 Set the *osnoise* tracer period in microseconds.8 Set the *osnoise* tracer period in microseconds.
@@ -15,3 +20,8 @@ · 11 unmodified lines
15 20
16 Stop the trace if the total sample is higher than the argument in microseconds.21 Stop the trace if the total sample is higher than the argument in microseconds.
17 If **-T** is set, it will also save the trace to the output.22 If **-T** is set, it will also save the trace to the output.
23
24**-T**, **--threshold** *us*
25
26 Specify the minimum delta between two time reads to be considered noise.
27 The default threshold is *5 us*.
Documentation/tools/rtla/common_timerlat_options.rst+12−0
@@ -1,3 +1,10 @@
1**-a**, **--auto** *us*
2
3 Set the automatic trace mode. This mode sets some commonly used options
4 while debugging the system. It is equivalent to use **-T** *us* **-s** *us*
5 **-t**. By default, *timerlat* tracer uses FIFO:95 for *timerlat* threads,
6 thus equilavent to **-P** *f:95*.
7
1**-p**, **--period** *us*8**-p**, **--period** *us*
2 9
3 Set the *timerlat* tracer period in microseconds.10 Set the *timerlat* tracer period in microseconds.
@@ -14,3 +21,8 @@ · 10 unmodified lines
14 21
15 Save the stack trace at the *IRQ* if a *Thread* latency is higher than the22 Save the stack trace at the *IRQ* if a *Thread* latency is higher than the
16 argument in us.23 argument in us.
24
25**--dma-latency** *us*
26 Set the /dev/cpu_dma_latency to *us*, aiming to bound exit from idle latencies.
27 *cyclictest* sets this value to *0* by default, use **--dma-latency** *0* to have
28 similar results.
tools/tracing/rtla/src/osnoise.c+83−0
@@ -655,6 +655,85 @@ void osnoise_put_print_stack(struct osnoise_context *context) · 654 unmodified lines
655 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL;655 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL;
656}656}
657 657
658/*
659 * osnoise_get_tracing_thresh - read and save the original "tracing_thresh"
660 */
661static long long
662osnoise_get_tracing_thresh(struct osnoise_context *context)
663{
664 long long tracing_thresh;
665
666 if (context->tracing_thresh != OSNOISE_OPTION_INIT_VAL)
667 return context->tracing_thresh;
668
669 if (context->orig_tracing_thresh != OSNOISE_OPTION_INIT_VAL)
670 return context->orig_tracing_thresh;
671
672 tracing_thresh = osnoise_read_ll_config("tracing_thresh");
673 if (tracing_thresh < 0)
674 goto out_err;
675
676 context->orig_tracing_thresh = tracing_thresh;
677 return tracing_thresh;
678
679out_err:
680 return OSNOISE_OPTION_INIT_VAL;
681}
682
683/*
684 * osnoise_set_tracing_thresh - set "tracing_thresh"
685 */
686int osnoise_set_tracing_thresh(struct osnoise_context *context, long long tracing_thresh)
687{
688 long long curr_tracing_thresh = osnoise_get_tracing_thresh(context);
689 int retval;
690
691 if (curr_tracing_thresh == OSNOISE_OPTION_INIT_VAL)
692 return -1;
693
694 retval = osnoise_write_ll_config("tracing_thresh", tracing_thresh);
695 if (retval < 0)
696 return -1;
697
698 context->tracing_thresh = tracing_thresh;
699
700 return 0;
701}
702
703/*
704 * osnoise_restore_tracing_thresh - restore the original "tracing_thresh"
705 */
706void osnoise_restore_tracing_thresh(struct osnoise_context *context)
707{
708 int retval;
709
710 if (context->orig_tracing_thresh == OSNOISE_OPTION_INIT_VAL)
711 return;
712
713 if (context->orig_tracing_thresh == context->tracing_thresh)
714 goto out_done;
715
716 retval = osnoise_write_ll_config("tracing_thresh", context->orig_tracing_thresh);
717 if (retval < 0)
718 err_msg("Could not restore original tracing_thresh\n");
719
720out_done:
721 context->tracing_thresh = OSNOISE_OPTION_INIT_VAL;
722}
723
724/*
725 * osnoise_put_tracing_thresh - restore original values and cleanup data
726 */
727void osnoise_put_tracing_thresh(struct osnoise_context *context)
728{
729 osnoise_restore_tracing_thresh(context);
730
731 if (context->orig_tracing_thresh == OSNOISE_OPTION_INIT_VAL)
732 return;
733
734 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL;
735}
736
658/*737/*
659 * enable_osnoise - enable osnoise tracer in the trace_instance738 * enable_osnoise - enable osnoise tracer in the trace_instance
660 */739 */
@@ -716,6 +795,9 @@ struct osnoise_context *osnoise_context_alloc(void) · 55 unmodified lines
716 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL;795 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL;
717 context->print_stack = OSNOISE_OPTION_INIT_VAL;796 context->print_stack = OSNOISE_OPTION_INIT_VAL;
718 797
798 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL;
799 context->tracing_thresh = OSNOISE_OPTION_INIT_VAL;
800
719 osnoise_get_context(context);801 osnoise_get_context(context);
720 802
721 return context;803 return context;
@@ -741,6 +823,7 @@ void osnoise_put_context(struct osnoise_context *context) · 19 unmodified lines
741 osnoise_put_stop_total_us(context);823 osnoise_put_stop_total_us(context);
742 osnoise_put_timerlat_period_us(context);824 osnoise_put_timerlat_period_us(context);
743 osnoise_put_print_stack(context);825 osnoise_put_print_stack(context);
826 osnoise_put_tracing_thresh(context);
744 827
745 free(context);828 free(context);
746}829}
tools/tracing/rtla/src/osnoise.h+8−0
@@ -23,6 +23,10 @@ struct osnoise_context { · 22 unmodified lines
23 long long orig_timerlat_period_us;23 long long orig_timerlat_period_us;
24 long long timerlat_period_us;24 long long timerlat_period_us;
25 25
26 /* 0 as init value */
27 long long orig_tracing_thresh;
28 long long tracing_thresh;
29
26 /* -1 as init value because 0 is disabled */30 /* -1 as init value because 0 is disabled */
27 long long orig_stop_us;31 long long orig_stop_us;
28 long long stop_us;32 long long stop_us;
@@ -67,6 +71,10 @@ int osnoise_set_timerlat_period_us(struct osnoise_context *context, · 38 unmodified lines
67 long long timerlat_period_us);71 long long timerlat_period_us);
68void osnoise_restore_timerlat_period_us(struct osnoise_context *context);72void osnoise_restore_timerlat_period_us(struct osnoise_context *context);
69 73
74int osnoise_set_tracing_thresh(struct osnoise_context *context,
75 long long tracing_thresh);
76void osnoise_restore_tracing_thresh(struct osnoise_context *context);
77
70void osnoise_restore_print_stack(struct osnoise_context *context);78void osnoise_restore_print_stack(struct osnoise_context *context);
71int osnoise_set_print_stack(struct osnoise_context *context,79int osnoise_set_print_stack(struct osnoise_context *context,
72 long long print_stack);80 long long print_stack);
tools/tracing/rtla/src/osnoise_hist.c+87−8
@@ -21,6 +21,7 @@ struct osnoise_hist_params { · 20 unmodified lines
21 char *trace_output;21 char *trace_output;
22 unsigned long long runtime;22 unsigned long long runtime;
23 unsigned long long period;23 unsigned long long period;
24 long long threshold;
24 long long stop_us;25 long long stop_us;
25 long long stop_total_us;26 long long stop_total_us;
26 int sleep_time;27 int sleep_time;
@@ -28,6 +29,7 @@ struct osnoise_hist_params { · 1 unmodified line
28 int set_sched;29 int set_sched;
29 int output_divisor;30 int output_divisor;
30 struct sched_attr sched_param;31 struct sched_attr sched_param;
32 struct trace_events *events;
31 33
32 char no_header;34 char no_header;
33 char no_summary;35 char no_summary;
@@ -425,19 +427,25 @@ static void osnoise_hist_usage(char *usage) · 391 unmodified lines
425 427
426 static const char * const msg[] = {428 static const char * const msg[] = {
427 "",429 "",
428 " usage: rtla osnoise hist [-h] [-D] [-d s] [-p us] [-r us] [-s us] [-S us] [-t[=file]] \\",430 " usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
429 " [-c cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\",431 " [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
430 " [--no-index] [--with-zeros]",432 " [-c cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] [--no-index] \\",
433 " [--with-zeros]",
431 "",434 "",
432 " -h/--help: print this menu",435 " -h/--help: print this menu",
436 " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
433 " -p/--period us: osnoise period in us",437 " -p/--period us: osnoise period in us",
434 " -r/--runtime us: osnoise runtime in us",438 " -r/--runtime us: osnoise runtime in us",
435 " -s/--stop us: stop trace if a single sample is higher than the argument in us",439 " -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",440 " -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
441 " -T/--threshold us: the minimum delta to be considered a noise",
437 " -c/--cpus cpu-list: list of cpus to run osnoise threads",442 " -c/--cpus cpu-list: list of cpus to run osnoise threads",
438 " -d/--duration time[s|m|h|d]: duration of the session",443 " -d/--duration time[s|m|h|d]: duration of the session",
439 " -D/--debug: print debug info",444 " -D/--debug: print debug info",
440 " -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]",445 " -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]",
446 " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
447 " --filter <filter>: enable a trace event filter to the previous -e event",
448 " --trigger <trigger>: enable a trace event trigger to the previous -e event",
441 " -b/--bucket-size N: set the histogram bucket size (default 1)",449 " -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)",450 " -E/--entries N: set the number of entries of the histogram (default 256)",
443 " --no-header: do not print header",451 " --no-header: do not print header",
@@ -471,6 +479,7 @@ static struct osnoise_hist_params · 27 unmodified lines
471*osnoise_hist_parse_args(int argc, char *argv[])479*osnoise_hist_parse_args(int argc, char *argv[])
472{480{
473 struct osnoise_hist_params *params;481 struct osnoise_hist_params *params;
482 struct trace_events *tevent;
474 int retval;483 int retval;
475 int c;484 int c;
476 485
@@ -485,6 +494,7 @@ static struct osnoise_hist_params · 8 unmodified lines
485 494
486 while (1) {495 while (1) {
487 static struct option long_options[] = {496 static struct option long_options[] = {
497 {"auto", required_argument, 0, 'a'},
488 {"bucket-size", required_argument, 0, 'b'},498 {"bucket-size", required_argument, 0, 'b'},
489 {"entries", required_argument, 0, 'E'},499 {"entries", required_argument, 0, 'E'},
490 {"cpus", required_argument, 0, 'c'},500 {"cpus", required_argument, 0, 'c'},
@@ -497,17 +507,21 @@ static struct osnoise_hist_params · 6 unmodified lines
497 {"stop", required_argument, 0, 's'},507 {"stop", required_argument, 0, 's'},
498 {"stop-total", required_argument, 0, 'S'},508 {"stop-total", required_argument, 0, 'S'},
499 {"trace", optional_argument, 0, 't'},509 {"trace", optional_argument, 0, 't'},
510 {"event", required_argument, 0, 'e'},
511 {"threshold", required_argument, 0, 'T'},
500 {"no-header", no_argument, 0, '0'},512 {"no-header", no_argument, 0, '0'},
501 {"no-summary", no_argument, 0, '1'},513 {"no-summary", no_argument, 0, '1'},
502 {"no-index", no_argument, 0, '2'},514 {"no-index", no_argument, 0, '2'},
503 {"with-zeros", no_argument, 0, '3'},515 {"with-zeros", no_argument, 0, '3'},
516 {"trigger", required_argument, 0, '4'},
517 {"filter", required_argument, 0, '5'},
504 {0, 0, 0, 0}518 {0, 0, 0, 0}
505 };519 };
506 520
507 /* getopt_long stores the option index here. */521 /* getopt_long stores the option index here. */
508 int option_index = 0;522 int option_index = 0;
509 523
510 c = getopt_long(argc, argv, "c:b:d:E:Dhp:P:r:s:S:t::0123",524 c = getopt_long(argc, argv, "a:c:b:d:e:E:Dhp:P:r:s:S:t::T:01234:5:",
511 long_options, &option_index);525 long_options, &option_index);
512 526
513 /* detect the end of the options. */527 /* detect the end of the options. */
@@ -515,6 +529,17 @@ static struct osnoise_hist_params · 1 unmodified line
515 break;529 break;
516 530
517 switch (c) {531 switch (c) {
532 case 'a':
533 /* set sample stop to auto_thresh */
534 params->stop_us = get_llong_from_str(optarg);
535
536 /* set sample threshold to 1 */
537 params->threshold = 1;
538
539 /* set trace */
540 params->trace_output = "osnoise_trace.txt";
541
542 break;
518 case 'b':543 case 'b':
519 params->bucket_size = get_llong_from_str(optarg);544 params->bucket_size = get_llong_from_str(optarg);
520 if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))545 if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))
@@ -534,6 +559,18 @@ static struct osnoise_hist_params · 13 unmodified lines
534 if (!params->duration)559 if (!params->duration)
535 osnoise_hist_usage("Invalid -D duration\n");560 osnoise_hist_usage("Invalid -D duration\n");
536 break;561 break;
562 case 'e':
563 tevent = trace_event_alloc(optarg);
564 if (!tevent) {
565 err_msg("Error alloc trace event");
566 exit(EXIT_FAILURE);
567 }
568
569 if (params->events)
570 tevent->next = params->events;
571
572 params->events = tevent;
573 break;
537 case 'E':574 case 'E':
538 params->entries = get_llong_from_str(optarg);575 params->entries = get_llong_from_str(optarg);
539 if ((params->entries < 10) || (params->entries > 9999999))576 if ((params->entries < 10) || (params->entries > 9999999))
@@ -565,6 +602,9 @@ static struct osnoise_hist_params · 25 unmodified lines
565 case 'S':602 case 'S':
566 params->stop_total_us = get_llong_from_str(optarg);603 params->stop_total_us = get_llong_from_str(optarg);
567 break;604 break;
605 case 'T':
606 params->threshold = get_llong_from_str(optarg);
607 break;
568 case 't':608 case 't':
569 if (optarg)609 if (optarg)
570 /* skip = */610 /* skip = */
@@ -584,6 +624,28 @@ static struct osnoise_hist_params · 13 unmodified lines
584 case '3': /* with zeros */624 case '3': /* with zeros */
585 params->with_zeros = 1;625 params->with_zeros = 1;
586 break;626 break;
627 case '4': /* trigger */
628 if (params->events) {
629 retval = trace_event_add_trigger(params->events, optarg);
630 if (retval) {
631 err_msg("Error adding trigger %s\n", optarg);
632 exit(EXIT_FAILURE);
633 }
634 } else {
635 osnoise_hist_usage("--trigger requires a previous -e\n");
636 }
637 break;
638 case '5': /* filter */
639 if (params->events) {
640 retval = trace_event_add_filter(params->events, optarg);
641 if (retval) {
642 err_msg("Error adding filter %s\n", optarg);
643 exit(EXIT_FAILURE);
644 }
645 } else {
646 osnoise_hist_usage("--filter requires a previous -e\n");
647 }
648 break;
587 default:649 default:
588 osnoise_hist_usage("Invalid option");650 osnoise_hist_usage("Invalid option");
589 }651 }
@@ -645,6 +707,14 @@ osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_hist_params · 55 unmodified lines
645 }707 }
646 }708 }
647 709
710 if (params->threshold) {
711 retval = osnoise_set_tracing_thresh(tool->context, params->threshold);
712 if (retval) {
713 err_msg("Failed to set tracing_thresh\n");
714 goto out_err;
715 }
716 }
717
648 return 0;718 return 0;
649 719
650out_err:720out_err:
@@ -751,6 +821,13 @@ int osnoise_hist_main(int argc, char *argv[]) · 100 unmodified lines
751 err_msg("Failed to enable the trace instance\n");821 err_msg("Failed to enable the trace instance\n");
752 goto out_hist;822 goto out_hist;
753 }823 }
824
825 if (params->events) {
826 retval = trace_events_enable(&record->trace, params->events);
827 if (retval)
828 goto out_hist;
829 }
830
754 trace_instance_start(&record->trace);831 trace_instance_start(&record->trace);
755 }832 }
756 833
@@ -771,9 +848,9 @@ int osnoise_hist_main(int argc, char *argv[]) · 14 unmodified lines
771 goto out_hist;848 goto out_hist;
772 }849 }
773 850
774 if (!tracefs_trace_is_on(trace->inst))851 if (trace_is_off(&tool->trace, &record->trace))
775 break;852 break;
776 };853 }
777 854
778 osnoise_read_trace_hist(tool);855 osnoise_read_trace_hist(tool);
779 856
@@ -781,8 +858,8 @@ int osnoise_hist_main(int argc, char *argv[]) · 1 unmodified line
781 858
782 return_value = 0;859 return_value = 0;
783 860
784 if (!tracefs_trace_is_on(trace->inst)) {861 if (trace_is_off(&tool->trace, &record->trace)) {
785 printf("rtla timelat hit stop tracing\n");862 printf("rtla osnoise hit stop tracing\n");
786 if (params->trace_output) {863 if (params->trace_output) {
787 printf(" Saving trace to %s\n", params->trace_output);864 printf(" Saving trace to %s\n", params->trace_output);
788 save_trace_to_file(record->trace.inst, params->trace_output);865 save_trace_to_file(record->trace.inst, params->trace_output);
@@ -790,6 +867,8 @@ int osnoise_hist_main(int argc, char *argv[]) · 1 unmodified line
790 }867 }
791 868
792out_hist:869out_hist:
870 trace_events_destroy(&record->trace, params->events);
871 params->events = NULL;
793 osnoise_free_histogram(tool->data);872 osnoise_free_histogram(tool->data);
794out_destroy:873out_destroy:
795 osnoise_destroy_tool(record);874 osnoise_destroy_tool(record);
tools/tracing/rtla/src/osnoise_top.c+85−6
@@ -23,6 +23,7 @@ struct osnoise_top_params { · 22 unmodified lines
23 char *trace_output;23 char *trace_output;
24 unsigned long long runtime;24 unsigned long long runtime;
25 unsigned long long period;25 unsigned long long period;
26 long long threshold;
26 long long stop_us;27 long long stop_us;
27 long long stop_total_us;28 long long stop_total_us;
28 int sleep_time;29 int sleep_time;
@@ -30,6 +31,7 @@ struct osnoise_top_params { · 1 unmodified line
30 int quiet;31 int quiet;
31 int set_sched;32 int set_sched;
32 struct sched_attr sched_param;33 struct sched_attr sched_param;
34 struct trace_events *events;
33};35};
34 36
35struct osnoise_top_cpu {37struct osnoise_top_cpu {
@@ -244,18 +246,24 @@ void osnoise_top_usage(char *usage) · 208 unmodified lines
244 int i;246 int i;
245 247
246 static const char * const msg[] = {248 static const char * const msg[] = {
247 " usage: rtla osnoise [top] [-h] [-q] [-D] [-d s] [-p us] [-r us] [-s us] [-S us] [-t[=file]] \\",249 " usage: rtla osnoise [top] [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
250 " [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
248 " [-c cpu-list] [-P priority]",251 " [-c cpu-list] [-P priority]",
249 "",252 "",
250 " -h/--help: print this menu",253 " -h/--help: print this menu",
254 " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
251 " -p/--period us: osnoise period in us",255 " -p/--period us: osnoise period in us",
252 " -r/--runtime us: osnoise runtime in us",256 " -r/--runtime us: osnoise runtime in us",
253 " -s/--stop us: stop trace if a single sample is higher than the argument in us",257 " -s/--stop us: stop trace if a single sample is higher than the argument in us",
254 " -S/--stop-total us: stop trace if the total sample is higher than the argument in us",258 " -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
259 " -T/--threshold us: the minimum delta to be considered a noise",
255 " -c/--cpus cpu-list: list of cpus to run osnoise threads",260 " -c/--cpus cpu-list: list of cpus to run osnoise threads",
256 " -d/--duration time[s|m|h|d]: duration of the session",261 " -d/--duration time[s|m|h|d]: duration of the session",
257 " -D/--debug: print debug info",262 " -D/--debug: print debug info",
258 " -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]",263 " -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]",
264 " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
265 " --filter <filter>: enable a trace event filter to the previous -e event",
266 " --trigger <trigger>: enable a trace event trigger to the previous -e event",
259 " -q/--quiet print only a summary at the end",267 " -q/--quiet print only a summary at the end",
260 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",268 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
261 " o:prio - use SCHED_OTHER with prio",269 " o:prio - use SCHED_OTHER with prio",
@@ -283,6 +291,7 @@ void osnoise_top_usage(char *usage) · 21 unmodified lines
283struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)291struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
284{292{
285 struct osnoise_top_params *params;293 struct osnoise_top_params *params;
294 struct trace_events *tevent;
286 int retval;295 int retval;
287 int c;296 int c;
288 297
@@ -292,9 +301,11 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) · 3 unmodified lines
292 301
293 while (1) {302 while (1) {
294 static struct option long_options[] = {303 static struct option long_options[] = {
304 {"auto", required_argument, 0, 'a'},
295 {"cpus", required_argument, 0, 'c'},305 {"cpus", required_argument, 0, 'c'},
296 {"debug", no_argument, 0, 'D'},306 {"debug", no_argument, 0, 'D'},
297 {"duration", required_argument, 0, 'd'},307 {"duration", required_argument, 0, 'd'},
308 {"event", required_argument, 0, 'e'},
298 {"help", no_argument, 0, 'h'},309 {"help", no_argument, 0, 'h'},
299 {"period", required_argument, 0, 'p'},310 {"period", required_argument, 0, 'p'},
300 {"priority", required_argument, 0, 'P'},311 {"priority", required_argument, 0, 'P'},
@@ -302,14 +313,17 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) · 1 unmodified line
302 {"runtime", required_argument, 0, 'r'},313 {"runtime", required_argument, 0, 'r'},
303 {"stop", required_argument, 0, 's'},314 {"stop", required_argument, 0, 's'},
304 {"stop-total", required_argument, 0, 'S'},315 {"stop-total", required_argument, 0, 'S'},
316 {"threshold", required_argument, 0, 'T'},
305 {"trace", optional_argument, 0, 't'},317 {"trace", optional_argument, 0, 't'},
318 {"trigger", required_argument, 0, '0'},
319 {"filter", required_argument, 0, '1'},
306 {0, 0, 0, 0}320 {0, 0, 0, 0}
307 };321 };
308 322
309 /* getopt_long stores the option index here. */323 /* getopt_long stores the option index here. */
310 int option_index = 0;324 int option_index = 0;
311 325
312 c = getopt_long(argc, argv, "c:d:Dhp:P:qr:s:S:t::",326 c = getopt_long(argc, argv, "a:c:d:De:hp:P:qr:s:S:t::T:0:1:",
313 long_options, &option_index);327 long_options, &option_index);
314 328
315 /* Detect the end of the options. */329 /* Detect the end of the options. */
@@ -317,6 +331,17 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) · 1 unmodified line
317 break;331 break;
318 332
319 switch (c) {333 switch (c) {
334 case 'a':
335 /* set sample stop to auto_thresh */
336 params->stop_us = get_llong_from_str(optarg);
337
338 /* set sample threshold to 1 */
339 params->threshold = 1;
340
341 /* set trace */
342 params->trace_output = "osnoise_trace.txt";
343
344 break;
320 case 'c':345 case 'c':
321 retval = parse_cpu_list(optarg, &params->monitored_cpus);346 retval = parse_cpu_list(optarg, &params->monitored_cpus);
322 if (retval)347 if (retval)
@@ -331,6 +356,18 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) · 8 unmodified lines
331 if (!params->duration)356 if (!params->duration)
332 osnoise_top_usage("Invalid -D duration\n");357 osnoise_top_usage("Invalid -D duration\n");
333 break;358 break;
359 case 'e':
360 tevent = trace_event_alloc(optarg);
361 if (!tevent) {
362 err_msg("Error alloc trace event");
363 exit(EXIT_FAILURE);
364 }
365
366 if (params->events)
367 tevent->next = params->events;
368 params->events = tevent;
369
370 break;
334 case 'h':371 case 'h':
335 case '?':372 case '?':
336 osnoise_top_usage(NULL);373 osnoise_top_usage(NULL);
@@ -367,6 +404,31 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) · 30 unmodified lines
367 else404 else
368 params->trace_output = "osnoise_trace.txt";405 params->trace_output = "osnoise_trace.txt";
369 break;406 break;
407 case 'T':
408 params->threshold = get_llong_from_str(optarg);
409 break;
410 case '0': /* trigger */
411 if (params->events) {
412 retval = trace_event_add_trigger(params->events, optarg);
413 if (retval) {
414 err_msg("Error adding trigger %s\n", optarg);
415 exit(EXIT_FAILURE);
416 }
417 } else {
418 osnoise_top_usage("--trigger requires a previous -e\n");
419 }
420 break;
421 case '1': /* filter */
422 if (params->events) {
423 retval = trace_event_add_filter(params->events, optarg);
424 if (retval) {
425 err_msg("Error adding filter %s\n", optarg);
426 exit(EXIT_FAILURE);
427 }
428 } else {
429 osnoise_top_usage("--filter requires a previous -e\n");
430 }
431 break;
370 default:432 default:
371 osnoise_top_usage("Invalid option");433 osnoise_top_usage("Invalid option");
372 }434 }
@@ -425,6 +487,14 @@ osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_top_params *p · 52 unmodified lines
425 }487 }
426 }488 }
427 489
490 if (params->threshold) {
491 retval = osnoise_set_tracing_thresh(tool->context, params->threshold);
492 if (retval) {
493 err_msg("Failed to set tracing_thresh\n");
494 goto out_err;
495 }
496 }
497
428 return 0;498 return 0;
429 499
430out_err:500out_err:
@@ -529,13 +599,20 @@ int osnoise_top_main(int argc, char **argv) · 98 unmodified lines
529 err_msg("Failed to enable the trace instance\n");599 err_msg("Failed to enable the trace instance\n");
530 goto out_top;600 goto out_top;
531 }601 }
602
603 if (params->events) {
604 retval = trace_events_enable(&record->trace, params->events);
605 if (retval)
606 goto out_top;
607 }
608
532 trace_instance_start(&record->trace);609 trace_instance_start(&record->trace);
533 }610 }
534 611
535 tool->start_time = time(NULL);612 tool->start_time = time(NULL);
536 osnoise_top_set_signals(params);613 osnoise_top_set_signals(params);
537 614
538 do {615 while (!stop_tracing) {
539 sleep(params->sleep_time);616 sleep(params->sleep_time);
540 617
541 retval = tracefs_iterate_raw_events(trace->tep,618 retval = tracefs_iterate_raw_events(trace->tep,
@@ -552,16 +629,16 @@ int osnoise_top_main(int argc, char **argv) · 10 unmodified lines
552 if (!params->quiet)629 if (!params->quiet)
553 osnoise_print_stats(params, tool);630 osnoise_print_stats(params, tool);
554 631
555 if (!tracefs_trace_is_on(trace->inst))632 if (trace_is_off(&tool->trace, &record->trace))
556 break;633 break;
557 634
558 } while (!stop_tracing);635 }
559 636
560 osnoise_print_stats(params, tool);637 osnoise_print_stats(params, tool);
561 638
562 return_value = 0;639 return_value = 0;
563 640
564 if (!tracefs_trace_is_on(trace->inst)) {641 if (trace_is_off(&tool->trace, &record->trace)) {
565 printf("osnoise hit stop tracing\n");642 printf("osnoise hit stop tracing\n");
566 if (params->trace_output) {643 if (params->trace_output) {
567 printf(" Saving trace to %s\n", params->trace_output);644 printf(" Saving trace to %s\n", params->trace_output);
@@ -570,6 +647,8 @@ int osnoise_top_main(int argc, char **argv) · 2 unmodified lines
570 }647 }
571 648
572out_top:649out_top:
650 trace_events_destroy(&record->trace, params->events);
651 params->events = NULL;
573 osnoise_free_top(tool->data);652 osnoise_free_top(tool->data);
574 osnoise_destroy_tool(record);653 osnoise_destroy_tool(record);
575 osnoise_destroy_tool(tool);654 osnoise_destroy_tool(tool);
tools/tracing/rtla/src/timerlat_hist.c+100−8
@@ -28,7 +28,9 @@ struct timerlat_hist_params { · 27 unmodified lines
28 int output_divisor;28 int output_divisor;
29 int duration;29 int duration;
30 int set_sched;30 int set_sched;
31 int dma_latency;
31 struct sched_attr sched_param;32 struct sched_attr sched_param;
33 struct trace_events *events;
32 34
33 char no_irq;35 char no_irq;
34 char no_thread;36 char no_thread;
@@ -428,11 +430,13 @@ static void timerlat_hist_usage(char *usage) · 393 unmodified lines
428 430
429 char *msg[] = {431 char *msg[] = {
430 "",432 "",
431 " usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] [-t[=file]] \\",433 " usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\",
432 " [-c cpu-list] [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",434 " [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] \\",
433 " [--no-index] [--with-zeros]",435 " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",
436 " [--no-index] [--with-zeros] [--dma-latency us]",
434 "",437 "",
435 " -h/--help: print this menu",438 " -h/--help: print this menu",
439 " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
436 " -p/--period us: timerlat period in us",440 " -p/--period us: timerlat period in us",
437 " -i/--irq us: stop trace if the irq latency is higher than the argument in us",441 " -i/--irq us: stop trace if the irq latency is higher than the argument in us",
438 " -T/--thread us: stop trace if the thread latency is higher than the argument in us",442 " -T/--thread us: stop trace if the thread latency is higher than the argument in us",
@@ -440,7 +444,10 @@ static void timerlat_hist_usage(char *usage) · 1 unmodified line
440 " -c/--cpus cpus: run the tracer only on the given cpus",444 " -c/--cpus cpus: run the tracer only on the given cpus",
441 " -d/--duration time[m|h|d]: duration of the session in seconds",445 " -d/--duration time[m|h|d]: duration of the session in seconds",
442 " -D/--debug: print debug info",446 " -D/--debug: print debug info",
443 " -T/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]",447 " -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]",
448 " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
449 " --filter <filter>: enable a trace event filter to the previous -e event",
450 " --trigger <trigger>: enable a trace event trigger to the previous -e event",
444 " -n/--nano: display data in nanoseconds",451 " -n/--nano: display data in nanoseconds",
445 " -b/--bucket-size N: set the histogram bucket size (default 1)",452 " -b/--bucket-size N: set the histogram bucket size (default 1)",
446 " -E/--entries N: set the number of entries of the histogram (default 256)",453 " -E/--entries N: set the number of entries of the histogram (default 256)",
@@ -450,6 +457,7 @@ static void timerlat_hist_usage(char *usage) · 3 unmodified lines
450 " --no-summary: do not print summary",457 " --no-summary: do not print summary",
451 " --no-index: do not print index",458 " --no-index: do not print index",
452 " --with-zeros: print zero only entries",459 " --with-zeros: print zero only entries",
460 " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",
453 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",461 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
454 " o:prio - use SCHED_OTHER with prio",462 " o:prio - use SCHED_OTHER with prio",
455 " r:prio - use SCHED_RR with prio",463 " r:prio - use SCHED_RR with prio",
@@ -477,6 +485,8 @@ static struct timerlat_hist_params · 21 unmodified lines
477*timerlat_hist_parse_args(int argc, char *argv[])485*timerlat_hist_parse_args(int argc, char *argv[])
478{486{
479 struct timerlat_hist_params *params;487 struct timerlat_hist_params *params;
488 struct trace_events *tevent;
489 int auto_thresh;
480 int retval;490 int retval;
481 int c;491 int c;
482 492
@@ -484,6 +494,9 @@ static struct timerlat_hist_params · 1 unmodified line
484 if (!params)494 if (!params)
485 exit(1);495 exit(1);
486 496
497 /* disabled by default */
498 params->dma_latency = -1;
499
487 /* display data in microseconds */500 /* display data in microseconds */
488 params->output_divisor = 1000;501 params->output_divisor = 1000;
489 params->bucket_size = 1;502 params->bucket_size = 1;
@@ -491,6 +504,7 @@ static struct timerlat_hist_params · 1 unmodified line
491 504
492 while (1) {505 while (1) {
493 static struct option long_options[] = {506 static struct option long_options[] = {
507 {"auto", required_argument, 0, 'a'},
494 {"cpus", required_argument, 0, 'c'},508 {"cpus", required_argument, 0, 'c'},
495 {"bucket-size", required_argument, 0, 'b'},509 {"bucket-size", required_argument, 0, 'b'},
496 {"debug", no_argument, 0, 'D'},510 {"debug", no_argument, 0, 'D'},
@@ -504,19 +518,23 @@ static struct timerlat_hist_params · 7 unmodified lines
504 {"stack", required_argument, 0, 's'},518 {"stack", required_argument, 0, 's'},
505 {"thread", required_argument, 0, 'T'},519 {"thread", required_argument, 0, 'T'},
506 {"trace", optional_argument, 0, 't'},520 {"trace", optional_argument, 0, 't'},
521 {"event", required_argument, 0, 'e'},
507 {"no-irq", no_argument, 0, '0'},522 {"no-irq", no_argument, 0, '0'},
508 {"no-thread", no_argument, 0, '1'},523 {"no-thread", no_argument, 0, '1'},
509 {"no-header", no_argument, 0, '2'},524 {"no-header", no_argument, 0, '2'},
510 {"no-summary", no_argument, 0, '3'},525 {"no-summary", no_argument, 0, '3'},
511 {"no-index", no_argument, 0, '4'},526 {"no-index", no_argument, 0, '4'},
512 {"with-zeros", no_argument, 0, '5'},527 {"with-zeros", no_argument, 0, '5'},
528 {"trigger", required_argument, 0, '6'},
529 {"filter", required_argument, 0, '7'},
530 {"dma-latency", required_argument, 0, '8'},
513 {0, 0, 0, 0}531 {0, 0, 0, 0}
514 };532 };
515 533
516 /* getopt_long stores the option index here. */534 /* getopt_long stores the option index here. */
517 int option_index = 0;535 int option_index = 0;
518 536
519 c = getopt_long(argc, argv, "c:b:d:E:Dhi:np:P:s:t::T:012345",537 c = getopt_long(argc, argv, "a:c:b:d:e:E:Dhi:np:P:s:t::T:0123456:7:8:",
520 long_options, &option_index);538 long_options, &option_index);
521 539
522 /* detect the end of the options. */540 /* detect the end of the options. */
@@ -524,6 +542,19 @@ static struct timerlat_hist_params · 1 unmodified line
524 break;542 break;
525 543
526 switch (c) {544 switch (c) {
545 case 'a':
546 auto_thresh = get_llong_from_str(optarg);
547
548 /* set thread stop to auto_thresh */
549 params->stop_total_us = auto_thresh;
550
551 /* get stack trace */
552 params->print_stack = auto_thresh;
553
554 /* set trace */
555 params->trace_output = "timerlat_trace.txt";
556
557 break;
527 case 'c':558 case 'c':
528 retval = parse_cpu_list(optarg, &params->monitored_cpus);559 retval = parse_cpu_list(optarg, &params->monitored_cpus);
529 if (retval)560 if (retval)
@@ -543,6 +574,18 @@ static struct timerlat_hist_params · 13 unmodified lines
543 if (!params->duration)574 if (!params->duration)
544 timerlat_hist_usage("Invalid -D duration\n");575 timerlat_hist_usage("Invalid -D duration\n");
545 break;576 break;
577 case 'e':
578 tevent = trace_event_alloc(optarg);
579 if (!tevent) {
580 err_msg("Error alloc trace event");
581 exit(EXIT_FAILURE);
582 }
583
584 if (params->events)
585 tevent->next = params->events;
586
587 params->events = tevent;
588 break;
546 case 'E':589 case 'E':
547 params->entries = get_llong_from_str(optarg);590 params->entries = get_llong_from_str(optarg);
548 if ((params->entries < 10) || (params->entries > 9999999))591 if ((params->entries < 10) || (params->entries > 9999999))
@@ -600,6 +643,35 @@ static struct timerlat_hist_params · 51 unmodified lines
600 case '5': /* with zeros */643 case '5': /* with zeros */
601 params->with_zeros = 1;644 params->with_zeros = 1;
602 break;645 break;
646 case '6': /* trigger */
647 if (params->events) {
648 retval = trace_event_add_trigger(params->events, optarg);
649 if (retval) {
650 err_msg("Error adding trigger %s\n", optarg);
651 exit(EXIT_FAILURE);
652 }
653 } else {
654 timerlat_hist_usage("--trigger requires a previous -e\n");
655 }
656 break;
657 case '7': /* filter */
658 if (params->events) {
659 retval = trace_event_add_filter(params->events, optarg);
660 if (retval) {
661 err_msg("Error adding filter %s\n", optarg);
662 exit(EXIT_FAILURE);
663 }
664 } else {
665 timerlat_hist_usage("--filter requires a previous -e\n");
666 }
667 break;
668 case '8':
669 params->dma_latency = get_llong_from_str(optarg);
670 if (params->dma_latency < 0 || params->dma_latency > 10000) {
671 err_msg("--dma-latency needs to be >= 0 and < 10000");
672 exit(EXIT_FAILURE);
673 }
674 break;
603 default:675 default:
604 timerlat_hist_usage("Invalid option");676 timerlat_hist_usage("Invalid option");
605 }677 }
@@ -732,6 +804,7 @@ int timerlat_hist_main(int argc, char *argv[]) · 126 unmodified lines
732 struct osnoise_tool *record = NULL;804 struct osnoise_tool *record = NULL;
733 struct osnoise_tool *tool = NULL;805 struct osnoise_tool *tool = NULL;
734 struct trace_instance *trace;806 struct trace_instance *trace;
807 int dma_latency_fd = -1;
735 int return_value = 1;808 int return_value = 1;
736 int retval;809 int retval;
737 810
@@ -767,6 +840,14 @@ int timerlat_hist_main(int argc, char *argv[]) · 29 unmodified lines
767 }840 }
768 }841 }
769 842
843 if (params->dma_latency >= 0) {
844 dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
845 if (dma_latency_fd < 0) {
846 err_msg("Could not set /dev/cpu_dma_latency.\n");
847 goto out_hist;
848 }
849 }
850
770 trace_instance_start(trace);851 trace_instance_start(trace);
771 852
772 if (params->trace_output) {853 if (params->trace_output) {
@@ -775,6 +856,13 @@ int timerlat_hist_main(int argc, char *argv[]) · 2 unmodified lines
775 err_msg("Failed to enable the trace instance\n");856 err_msg("Failed to enable the trace instance\n");
776 goto out_hist;857 goto out_hist;
777 }858 }
859
860 if (params->events) {
861 retval = trace_events_enable(&record->trace, params->events);
862 if (retval)
863 goto out_hist;
864 }
865
778 trace_instance_start(&record->trace);866 trace_instance_start(&record->trace);
779 }867 }
780 868
@@ -795,15 +883,15 @@ int timerlat_hist_main(int argc, char *argv[]) · 14 unmodified lines
795 goto out_hist;883 goto out_hist;
796 }884 }
797 885
798 if (!tracefs_trace_is_on(trace->inst))886 if (trace_is_off(&tool->trace, &record->trace))
799 break;887 break;
800 };888 }
801 889
802 timerlat_print_stats(params, tool);890 timerlat_print_stats(params, tool);
803 891
804 return_value = 0;892 return_value = 0;
805 893
806 if (!tracefs_trace_is_on(trace->inst)) {894 if (trace_is_off(&tool->trace, &record->trace)) {
807 printf("rtla timelat hit stop tracing\n");895 printf("rtla timelat hit stop tracing\n");
808 if (params->trace_output) {896 if (params->trace_output) {
809 printf(" Saving trace to %s\n", params->trace_output);897 printf(" Saving trace to %s\n", params->trace_output);
@@ -812,6 +900,10 @@ int timerlat_hist_main(int argc, char *argv[]) · 2 unmodified lines
812 }900 }
813 901
814out_hist:902out_hist:
903 if (dma_latency_fd >= 0)
904 close(dma_latency_fd);
905 trace_events_destroy(&record->trace, params->events);
906 params->events = NULL;
815 timerlat_free_histogram(tool->data);907 timerlat_free_histogram(tool->data);
816 osnoise_destroy_tool(record);908 osnoise_destroy_tool(record);
817 osnoise_destroy_tool(tool);909 osnoise_destroy_tool(tool);
tools/tracing/rtla/src/timerlat_top.c+97−6
@@ -29,7 +29,9 @@ struct timerlat_top_params { · 28 unmodified lines
29 int duration;29 int duration;
30 int quiet;30 int quiet;
31 int set_sched;31 int set_sched;
32 int dma_latency;
32 struct sched_attr sched_param;33 struct sched_attr sched_param;
34 struct trace_events *events;
33};35};
34 36
35struct timerlat_top_cpu {37struct timerlat_top_cpu {
@@ -266,10 +268,12 @@ static void timerlat_top_usage(char *usage) · 230 unmodified lines
266 268
267 static const char *const msg[] = {269 static const char *const msg[] = {
268 "",270 "",
269 " usage: rtla timerlat [top] [-h] [-q] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] [-t[=file]] \\",271 " usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\",
270 " [-c cpu-list] [-P priority]",272 " [[-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] \\",
273 " [-P priority] [--dma-latency us]",
271 "",274 "",
272 " -h/--help: print this menu",275 " -h/--help: print this menu",
276 " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
273 " -p/--period us: timerlat period in us",277 " -p/--period us: timerlat period in us",
274 " -i/--irq us: stop trace if the irq latency is higher than the argument in us",278 " -i/--irq us: stop trace if the irq latency is higher than the argument in us",
275 " -T/--thread us: stop trace if the thread latency is higher than the argument in us",279 " -T/--thread us: stop trace if the thread latency is higher than the argument in us",
@@ -278,8 +282,12 @@ static void timerlat_top_usage(char *usage) · 2 unmodified lines
278 " -d/--duration time[m|h|d]: duration of the session in seconds",282 " -d/--duration time[m|h|d]: duration of the session in seconds",
279 " -D/--debug: print debug info",283 " -D/--debug: print debug info",
280 " -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]",284 " -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]",
285 " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
286 " --filter <command>: enable a trace event filter to the previous -e event",
287 " --trigger <command>: enable a trace event trigger to the previous -e event",
281 " -n/--nano: display data in nanoseconds",288 " -n/--nano: display data in nanoseconds",
282 " -q/--quiet print only a summary at the end",289 " -q/--quiet print only a summary at the end",
290 " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",
283 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",291 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
284 " o:prio - use SCHED_OTHER with prio",292 " o:prio - use SCHED_OTHER with prio",
285 " r:prio - use SCHED_RR with prio",293 " r:prio - use SCHED_RR with prio",
@@ -307,6 +315,8 @@ static struct timerlat_top_params · 21 unmodified lines
307*timerlat_top_parse_args(int argc, char **argv)315*timerlat_top_parse_args(int argc, char **argv)
308{316{
309 struct timerlat_top_params *params;317 struct timerlat_top_params *params;
318 struct trace_events *tevent;
319 long long auto_thresh;
310 int retval;320 int retval;
311 int c;321 int c;
312 322
@@ -314,14 +324,19 @@ static struct timerlat_top_params · 1 unmodified line
314 if (!params)324 if (!params)
315 exit(1);325 exit(1);
316 326
327 /* disabled by default */
328 params->dma_latency = -1;
329
317 /* display data in microseconds */330 /* display data in microseconds */
318 params->output_divisor = 1000;331 params->output_divisor = 1000;
319 332
320 while (1) {333 while (1) {
321 static struct option long_options[] = {334 static struct option long_options[] = {
335 {"auto", required_argument, 0, 'a'},
322 {"cpus", required_argument, 0, 'c'},336 {"cpus", required_argument, 0, 'c'},
323 {"debug", no_argument, 0, 'D'},337 {"debug", no_argument, 0, 'D'},
324 {"duration", required_argument, 0, 'd'},338 {"duration", required_argument, 0, 'd'},
339 {"event", required_argument, 0, 'e'},
325 {"help", no_argument, 0, 'h'},340 {"help", no_argument, 0, 'h'},
326 {"irq", required_argument, 0, 'i'},341 {"irq", required_argument, 0, 'i'},
327 {"nano", no_argument, 0, 'n'},342 {"nano", no_argument, 0, 'n'},
@@ -331,13 +346,16 @@ static struct timerlat_top_params · 3 unmodified lines
331 {"stack", required_argument, 0, 's'},346 {"stack", required_argument, 0, 's'},
332 {"thread", required_argument, 0, 'T'},347 {"thread", required_argument, 0, 'T'},
333 {"trace", optional_argument, 0, 't'},348 {"trace", optional_argument, 0, 't'},
349 {"trigger", required_argument, 0, '0'},
350 {"filter", required_argument, 0, '1'},
351 {"dma-latency", required_argument, 0, '2'},
334 {0, 0, 0, 0}352 {0, 0, 0, 0}
335 };353 };
336 354
337 /* getopt_long stores the option index here. */355 /* getopt_long stores the option index here. */
338 int option_index = 0;356 int option_index = 0;
339 357
340 c = getopt_long(argc, argv, "c:d:Dhi:np:P:qs:t::T:",358 c = getopt_long(argc, argv, "a:c:d:De:hi:np:P:qs:t::T:0:1:2:",
341 long_options, &option_index);359 long_options, &option_index);
342 360
343 /* detect the end of the options. */361 /* detect the end of the options. */
@@ -345,6 +363,19 @@ static struct timerlat_top_params · 1 unmodified line
345 break;363 break;
346 364
347 switch (c) {365 switch (c) {
366 case 'a':
367 auto_thresh = get_llong_from_str(optarg);
368
369 /* set thread stop to auto_thresh */
370 params->stop_total_us = auto_thresh;
371
372 /* get stack trace */
373 params->print_stack = auto_thresh;
374
375 /* set trace */
376 params->trace_output = "timerlat_trace.txt";
377
378 break;
348 case 'c':379 case 'c':
349 retval = parse_cpu_list(optarg, &params->monitored_cpus);380 retval = parse_cpu_list(optarg, &params->monitored_cpus);
350 if (retval)381 if (retval)
@@ -359,6 +390,17 @@ static struct timerlat_top_params · 8 unmodified lines
359 if (!params->duration)390 if (!params->duration)
360 timerlat_top_usage("Invalid -D duration\n");391 timerlat_top_usage("Invalid -D duration\n");
361 break;392 break;
393 case 'e':
394 tevent = trace_event_alloc(optarg);
395 if (!tevent) {
396 err_msg("Error alloc trace event");
397 exit(EXIT_FAILURE);
398 }
399
400 if (params->events)
401 tevent->next = params->events;
402 params->events = tevent;
403 break;
362 case 'h':404 case 'h':
363 case '?':405 case '?':
364 timerlat_top_usage(NULL);406 timerlat_top_usage(NULL);
@@ -396,6 +438,35 @@ static struct timerlat_top_params · 31 unmodified lines
396 else438 else
397 params->trace_output = "timerlat_trace.txt";439 params->trace_output = "timerlat_trace.txt";
398 break;440 break;
441 case '0': /* trigger */
442 if (params->events) {
443 retval = trace_event_add_trigger(params->events, optarg);
444 if (retval) {
445 err_msg("Error adding trigger %s\n", optarg);
446 exit(EXIT_FAILURE);
447 }
448 } else {
449 timerlat_top_usage("--trigger requires a previous -e\n");
450 }
451 break;
452 case '1': /* filter */
453 if (params->events) {
454 retval = trace_event_add_filter(params->events, optarg);
455 if (retval) {
456 err_msg("Error adding filter %s\n", optarg);
457 exit(EXIT_FAILURE);
458 }
459 } else {
460 timerlat_top_usage("--filter requires a previous -e\n");
461 }
462 break;
463 case '2': /* dma-latency */
464 params->dma_latency = get_llong_from_str(optarg);
465 if (params->dma_latency < 0 || params->dma_latency > 10000) {
466 err_msg("--dma-latency needs to be >= 0 and < 10000");
467 exit(EXIT_FAILURE);
468 }
469 break;
399 default:470 default:
400 timerlat_top_usage("Invalid option");471 timerlat_top_usage("Invalid option");
401 }472 }
@@ -524,6 +595,7 @@ int timerlat_top_main(int argc, char *argv[]) · 122 unmodified lines
524 struct osnoise_tool *record = NULL;595 struct osnoise_tool *record = NULL;
525 struct osnoise_tool *top = NULL;596 struct osnoise_tool *top = NULL;
526 struct trace_instance *trace;597 struct trace_instance *trace;
598 int dma_latency_fd = -1;
527 int return_value = 1;599 int return_value = 1;
528 int retval;600 int retval;
529 601
@@ -559,6 +631,14 @@ int timerlat_top_main(int argc, char *argv[]) · 29 unmodified lines
559 }631 }
560 }632 }
561 633
634 if (params->dma_latency >= 0) {
635 dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
636 if (dma_latency_fd < 0) {
637 err_msg("Could not set /dev/cpu_dma_latency.\n");
638 goto out_top;
639 }
640 }
641
562 trace_instance_start(trace);642 trace_instance_start(trace);
563 643
564 if (params->trace_output) {644 if (params->trace_output) {
@@ -567,6 +647,13 @@ int timerlat_top_main(int argc, char *argv[]) · 2 unmodified lines
567 err_msg("Failed to enable the trace instance\n");647 err_msg("Failed to enable the trace instance\n");
568 goto out_top;648 goto out_top;
569 }649 }
650
651 if (params->events) {
652 retval = trace_events_enable(&record->trace, params->events);
653 if (retval)
654 goto out_top;
655 }
656
570 trace_instance_start(&record->trace);657 trace_instance_start(&record->trace);
571 }658 }
572 659
@@ -590,16 +677,16 @@ int timerlat_top_main(int argc, char *argv[]) · 17 unmodified lines
590 if (!params->quiet)677 if (!params->quiet)
591 timerlat_print_stats(params, top);678 timerlat_print_stats(params, top);
592 679
593 if (!tracefs_trace_is_on(trace->inst))680 if (trace_is_off(&top->trace, &record->trace))
594 break;681 break;
595 682
596 };683 }
597 684
598 timerlat_print_stats(params, top);685 timerlat_print_stats(params, top);
599 686
600 return_value = 0;687 return_value = 0;
601 688
602 if (!tracefs_trace_is_on(trace->inst)) {689 if (trace_is_off(&top->trace, &record->trace)) {
603 printf("rtla timelat hit stop tracing\n");690 printf("rtla timelat hit stop tracing\n");
604 if (params->trace_output) {691 if (params->trace_output) {
605 printf(" Saving trace to %s\n", params->trace_output);692 printf(" Saving trace to %s\n", params->trace_output);
@@ -608,6 +695,10 @@ int timerlat_top_main(int argc, char *argv[]) · 2 unmodified lines
608 }695 }
609 696
610out_top:697out_top:
698 if (dma_latency_fd >= 0)
699 close(dma_latency_fd);
700 trace_events_destroy(&record->trace, params->events);
701 params->events = NULL;
611 timerlat_free_top(top->data);702 timerlat_free_top(top->data);
612 osnoise_destroy_tool(record);703 osnoise_destroy_tool(record);
613 osnoise_destroy_tool(top);704 osnoise_destroy_tool(top);
tools/tracing/rtla/src/trace.c+345−0
@@ -190,3 +190,348 @@ int trace_instance_start(struct trace_instance *trace) · 189 unmodified lines
190{190{
191 return tracefs_trace_on(trace->inst);191 return tracefs_trace_on(trace->inst);
192}192}
193
194/*
195 * trace_events_free - free a list of trace events
196 */
197static void trace_events_free(struct trace_events *events)
198{
199 struct trace_events *tevent = events;
200 struct trace_events *free_event;
201
202 while (tevent) {
203 free_event = tevent;
204
205 tevent = tevent->next;
206
207 if (free_event->filter)
208 free(free_event->filter);
209 if (free_event->trigger)
210 free(free_event->trigger);
211 free(free_event->system);
212 free(free_event);
213 }
214}
215
216/*
217 * trace_event_alloc - alloc and parse a single trace event
218 */
219struct trace_events *trace_event_alloc(const char *event_string)
220{
221 struct trace_events *tevent;
222
223 tevent = calloc(1, sizeof(*tevent));
224 if (!tevent)
225 return NULL;
226
227 tevent->system = strdup(event_string);
228 if (!tevent->system) {
229 free(tevent);
230 return NULL;
231 }
232
233 tevent->event = strstr(tevent->system, ":");
234 if (tevent->event) {
235 *tevent->event = '\0';
236 tevent->event = &tevent->event[1];
237 }
238
239 return tevent;
240}
241
242/*
243 * trace_event_add_filter - record an event filter
244 */
245int trace_event_add_filter(struct trace_events *event, char *filter)
246{
247 if (event->filter)
248 free(event->filter);
249
250 event->filter = strdup(filter);
251 if (!event->filter)
252 return 1;
253
254 return 0;
255}
256
257/*
258 * trace_event_add_trigger - record an event trigger action
259 */
260int trace_event_add_trigger(struct trace_events *event, char *trigger)
261{
262 if (event->trigger)
263 free(event->trigger);
264
265 event->trigger = strdup(trigger);
266 if (!event->trigger)
267 return 1;
268
269 return 0;
270}
271
272/*
273 * trace_event_disable_filter - disable an event filter
274 */
275static void trace_event_disable_filter(struct trace_instance *instance,
276 struct trace_events *tevent)
277{
278 char filter[1024];
279 int retval;
280
281 if (!tevent->filter)
282 return;
283
284 if (!tevent->filter_enabled)
285 return;
286
287 debug_msg("Disabling %s:%s filter %s\n", tevent->system,
288 tevent->event ? : "*", tevent->filter);
289
290 snprintf(filter, 1024, "!%s\n", tevent->filter);
291
292 retval = tracefs_event_file_write(instance->inst, tevent->system,
293 tevent->event, "filter", filter);
294 if (retval < 0)
295 err_msg("Error disabling %s:%s filter %s\n", tevent->system,
296 tevent->event ? : "*", tevent->filter);
297}
298
299/*
300 * trace_event_save_hist - save the content of an event hist
301 *
302 * If the trigger is a hist: one, save the content of the hist file.
303 */
304static void trace_event_save_hist(struct trace_instance *instance,
305 struct trace_events *tevent)
306{
307 int retval, index, out_fd;
308 mode_t mode = 0644;
309 char path[1024];
310 char *hist;
311
312 if (!tevent)
313 return;
314
315 /* trigger enables hist */
316 if (!tevent->trigger)
317 return;
318
319 /* is this a hist: trigger? */
320 retval = strncmp(tevent->trigger, "hist:", strlen("hist:"));
321 if (retval)
322 return;
323
324 snprintf(path, 1024, "%s_%s_hist.txt", tevent->system, tevent->event);
325
326 printf(" Saving event %s:%s hist to %s\n", tevent->system, tevent->event, path);
327
328 out_fd = creat(path, mode);
329 if (out_fd < 0) {
330 err_msg(" Failed to create %s output file\n", path);
331 return;
332 }
333
334 hist = tracefs_event_file_read(instance->inst, tevent->system, tevent->event, "hist", 0);
335 if (!hist) {
336 err_msg(" Failed to read %s:%s hist file\n", tevent->system, tevent->event);
337 goto out_close;
338 }
339
340 index = 0;
341 do {
342 index += write(out_fd, &hist[index], strlen(hist) - index);
343 } while (index < strlen(hist));
344
345 free(hist);
346out_close:
347 close(out_fd);
348}
349
350/*
351 * trace_event_disable_trigger - disable an event trigger
352 */
353static void trace_event_disable_trigger(struct trace_instance *instance,
354 struct trace_events *tevent)
355{
356 char trigger[1024];
357 int retval;
358
359 if (!tevent->trigger)
360 return;
361
362 if (!tevent->trigger_enabled)
363 return;
364
365 debug_msg("Disabling %s:%s trigger %s\n", tevent->system,
366 tevent->event ? : "*", tevent->trigger);
367
368 trace_event_save_hist(instance, tevent);
369
370 snprintf(trigger, 1024, "!%s\n", tevent->trigger);
371
372 retval = tracefs_event_file_write(instance->inst, tevent->system,
373 tevent->event, "trigger", trigger);
374 if (retval < 0)
375 err_msg("Error disabling %s:%s trigger %s\n", tevent->system,
376 tevent->event ? : "*", tevent->trigger);
377}
378
379/*
380 * trace_events_disable - disable all trace events
381 */
382void trace_events_disable(struct trace_instance *instance,
383 struct trace_events *events)
384{
385 struct trace_events *tevent = events;
386
387 if (!events)
388 return;
389
390 while (tevent) {
391 debug_msg("Disabling event %s:%s\n", tevent->system, tevent->event ? : "*");
392 if (tevent->enabled) {
393 trace_event_disable_filter(instance, tevent);
394 trace_event_disable_trigger(instance, tevent);
395 tracefs_event_disable(instance->inst, tevent->system, tevent->event);
396 }
397
398 tevent->enabled = 0;
399 tevent = tevent->next;
400 }
401}
402
403/*
404 * trace_event_enable_filter - enable an event filter associated with an event
405 */
406static int trace_event_enable_filter(struct trace_instance *instance,
407 struct trace_events *tevent)
408{
409 char filter[1024];
410 int retval;
411
412 if (!tevent->filter)
413 return 0;
414
415 if (!tevent->event) {
416 err_msg("Filter %s applies only for single events, not for all %s:* events\n",
417 tevent->filter, tevent->system);
418 return 1;
419 }
420
421 snprintf(filter, 1024, "%s\n", tevent->filter);
422
423 debug_msg("Enabling %s:%s filter %s\n", tevent->system,
424 tevent->event ? : "*", tevent->filter);
425
426 retval = tracefs_event_file_write(instance->inst, tevent->system,
427 tevent->event, "filter", filter);
428 if (retval < 0) {
429 err_msg("Error enabling %s:%s filter %s\n", tevent->system,
430 tevent->event ? : "*", tevent->filter);
431 return 1;
432 }
433
434 tevent->filter_enabled = 1;
435 return 0;
436}
437
438/*
439 * trace_event_enable_trigger - enable an event trigger associated with an event
440 */
441static int trace_event_enable_trigger(struct trace_instance *instance,
442 struct trace_events *tevent)
443{
444 char trigger[1024];
445 int retval;
446
447 if (!tevent->trigger)
448 return 0;
449
450 if (!tevent->event) {
451 err_msg("Trigger %s applies only for single events, not for all %s:* events\n",
452 tevent->trigger, tevent->system);
453 return 1;
454 }
455
456 snprintf(trigger, 1024, "%s\n", tevent->trigger);
457
458 debug_msg("Enabling %s:%s trigger %s\n", tevent->system,
459 tevent->event ? : "*", tevent->trigger);
460
461 retval = tracefs_event_file_write(instance->inst, tevent->system,
462 tevent->event, "trigger", trigger);
463 if (retval < 0) {
464 err_msg("Error enabling %s:%s trigger %s\n", tevent->system,
465 tevent->event ? : "*", tevent->trigger);
466 return 1;
467 }
468
469 tevent->trigger_enabled = 1;
470
471 return 0;
472}
473
474/*
475 * trace_events_enable - enable all events
476 */
477int trace_events_enable(struct trace_instance *instance,
478 struct trace_events *events)
479{
480 struct trace_events *tevent = events;
481 int retval;
482
483 while (tevent) {
484 debug_msg("Enabling event %s:%s\n", tevent->system, tevent->event ? : "*");
485 retval = tracefs_event_enable(instance->inst, tevent->system, tevent->event);
486 if (retval < 0) {
487 err_msg("Error enabling event %s:%s\n", tevent->system,
488 tevent->event ? : "*");
489 return 1;
490 }
491
492 retval = trace_event_enable_filter(instance, tevent);
493 if (retval)
494 return 1;
495
496 retval = trace_event_enable_trigger(instance, tevent);
497 if (retval)
498 return 1;
499
500 tevent->enabled = 1;
501 tevent = tevent->next;
502 }
503
504 return 0;
505}
506
507/*
508 * trace_events_destroy - disable and free all trace events
509 */
510void trace_events_destroy(struct trace_instance *instance,
511 struct trace_events *events)
512{
513 if (!events)
514 return;
515
516 trace_events_disable(instance, events);
517 trace_events_free(events);
518}
519
520int trace_is_off(struct trace_instance *tool, struct trace_instance *trace)
521{
522 /*
523 * The tool instance is always present, it is the one used to collect
524 * data.
525 */
526 if (!tracefs_trace_is_on(tool->inst))
527 return 1;
528
529 /*
530 * The trace instance is only enabled when -t is set. IOW, when the system
531 * is tracing.
532 */
533 if (trace && !tracefs_trace_is_on(trace->inst))
534 return 1;
535
536 return 0;
537}
tools/tracing/rtla/src/trace.h+23−0
@@ -2,6 +2,17 @@ · 1 unmodified line
2#include <tracefs.h>2#include <tracefs.h>
3#include <stddef.h>3#include <stddef.h>
4 4
5struct trace_events {
6 struct trace_events *next;
7 char *system;
8 char *event;
9 char *filter;
10 char *trigger;
11 char enabled;
12 char filter_enabled;
13 char trigger_enabled;
14};
15
5struct trace_instance {16struct trace_instance {
6 struct tracefs_instance *inst;17 struct tracefs_instance *inst;
7 struct tep_handle *tep;18 struct tep_handle *tep;
@@ -25,3 +36,15 @@ void destroy_instance(struct tracefs_instance *inst); · 17 unmodified lines
25int save_trace_to_file(struct tracefs_instance *inst, const char *filename);36int save_trace_to_file(struct tracefs_instance *inst, const char *filename);
26int collect_registered_events(struct tep_event *tep, struct tep_record *record,37int collect_registered_events(struct tep_event *tep, struct tep_record *record,
27 int cpu, void *context);38 int cpu, void *context);
39
40struct trace_events *trace_event_alloc(const char *event_string);
41void trace_events_disable(struct trace_instance *instance,
42 struct trace_events *events);
43void trace_events_destroy(struct trace_instance *instance,
44 struct trace_events *events);
45int trace_events_enable(struct trace_instance *instance,
46 struct trace_events *events);
47
48int trace_event_add_filter(struct trace_events *event, char *filter);
49int trace_event_add_trigger(struct trace_events *event, char *trigger);
50int trace_is_off(struct trace_instance *tool, struct trace_instance *trace);
tools/tracing/rtla/src/utils.c+33−0
@@ -10,6 +10,7 @@ · 9 unmodified lines
10#include <unistd.h>10#include <unistd.h>
11#include <ctype.h>11#include <ctype.h>
12#include <errno.h>12#include <errno.h>
13#include <fcntl.h>
13#include <sched.h>14#include <sched.h>
14#include <stdio.h>15#include <stdio.h>
15 16
@@ -431,3 +432,35 @@ int parse_prio(char *arg, struct sched_attr *sched_param) · 415 unmodified lines
431 }432 }
432 return 0;433 return 0;
433}434}
435
436/*
437 * set_cpu_dma_latency - set the /dev/cpu_dma_latecy
438 *
439 * This is used to reduce the exit from idle latency. The value
440 * will be reset once the file descriptor of /dev/cpu_dma_latecy
441 * is closed.
442 *
443 * Return: the /dev/cpu_dma_latecy file descriptor
444 */
445int set_cpu_dma_latency(int32_t latency)
446{
447 int retval;
448 int fd;
449
450 fd = open("/dev/cpu_dma_latency", O_RDWR);
451 if (fd < 0) {
452 err_msg("Error opening /dev/cpu_dma_latency\n");
453 return -1;
454 }
455
456 retval = write(fd, &latency, 4);
457 if (retval < 1) {
458 err_msg("Error setting /dev/cpu_dma_latency\n");
459 close(fd);
460 return -1;
461 }
462
463 debug_msg("Set /dev/cpu_dma_latency to %d\n", latency);
464
465 return fd;
466}
tools/tracing/rtla/src/utils.h+1−0
@@ -54,3 +54,4 @@ struct sched_attr { · 53 unmodified lines
54 54
55int parse_prio(char *arg, struct sched_attr *sched_param);55int parse_prio(char *arg, struct sched_attr *sched_param);
56int set_comm_sched_attr(const char *comm, struct sched_attr *attr);56int set_comm_sched_attr(const char *comm, struct sched_attr *attr);
57int set_cpu_dma_latency(int32_t latency);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t