Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'trace-tools-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull tracing tools updates from Steven Rostedt:

- Use total duration to calculate average in rtla osnoise_hist

- Use 2 digit precision for displaying average

- Print an intuitive auto analysis of timerlat results

- Add auto analysis to timerlat top

- Add hwnoise, which is the same as osnoise but focuses on hardware

- Small clean ups

* tag 'trace-tools-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
Documentation/rtla: Add hwnoise man page
rtla: Add hwnoise tool
Documentation/rtla: Add timerlat-top auto-analysis options
rtla/timerlat: Add auto-analysis support to timerlat top
rtla/timerlat: Add auto-analysis core
tools/tracing/rtla: osnoise_hist: display average with two-digit precision
tools/tracing/rtla: osnoise_hist: use total duration for average calculation
tools/rv: Remove unneeded semicolon

+1440 -111
+7
Documentation/tools/rtla/common_timerlat_aa.rst
··· 1 + **--dump-tasks** 2 + 3 + prints the task running on all CPUs if stop conditions are met (depends on !--no-aa) 4 + 5 + **--no-aa** 6 + 7 + disable auto-analysis, reducing rtla timerlat cpu usage
+1
Documentation/tools/rtla/index.rst
··· 17 17 rtla-timerlat 18 18 rtla-timerlat-hist 19 19 rtla-timerlat-top 20 + rtla-hwnoise 20 21 21 22 .. only:: subproject and html 22 23
+107
Documentation/tools/rtla/rtla-hwnoise.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ============ 4 + rtla-hwnoise 5 + ============ 6 + ------------------------------------------ 7 + Detect and quantify hardware-related noise 8 + ------------------------------------------ 9 + 10 + :Manual section: 1 11 + 12 + SYNOPSIS 13 + ======== 14 + 15 + **rtla hwnoise** [*OPTIONS*] 16 + 17 + DESCRIPTION 18 + =========== 19 + 20 + **rtla hwnoise** collects the periodic summary from the *osnoise* tracer 21 + running with *interrupts disabled*. By disabling interrupts, and the scheduling 22 + of threads as a consequence, only non-maskable interrupts and hardware-related 23 + noise is allowed. 24 + 25 + The tool also allows the configurations of the *osnoise* tracer and the 26 + collection of the tracer output. 27 + 28 + OPTIONS 29 + ======= 30 + .. include:: common_osnoise_options.rst 31 + 32 + .. include:: common_top_options.rst 33 + 34 + .. include:: common_options.rst 35 + 36 + EXAMPLE 37 + ======= 38 + In the example below, the **rtla hwnoise** tool is set to run on CPUs *1-7* 39 + on a system with 8 cores/16 threads with hyper-threading enabled. 40 + 41 + The tool is set to detect any noise higher than *one microsecond*, 42 + to run for *ten minutes*, displaying a summary of the report at the 43 + end of the session:: 44 + 45 + # rtla hwnoise -c 1-7 -T 1 -d 10m -q 46 + Hardware-related Noise 47 + duration: 0 00:10:00 | time is in us 48 + CPU Period Runtime Noise % CPU Aval Max Noise Max Single HW NMI 49 + 1 #599 599000000 138 99.99997 3 3 4 74 50 + 2 #599 599000000 85 99.99998 3 3 4 75 51 + 3 #599 599000000 86 99.99998 4 3 6 75 52 + 4 #599 599000000 81 99.99998 4 4 2 75 53 + 5 #599 599000000 85 99.99998 2 2 2 75 54 + 6 #599 599000000 76 99.99998 2 2 0 75 55 + 7 #599 599000000 77 99.99998 3 3 0 75 56 + 57 + 58 + The first column shows the *CPU*, and the second column shows how many 59 + *Periods* the tool ran during the session. The *Runtime* is the time 60 + the tool effectively runs on the CPU. The *Noise* column is the sum of 61 + all noise that the tool observed, and the *% CPU Aval* is the relation 62 + between the *Runtime* and *Noise*. 63 + 64 + The *Max Noise* column is the maximum hardware noise the tool detected in a 65 + single period, and the *Max Single* is the maximum single noise seen. 66 + 67 + The *HW* and *NMI* columns show the total number of *hardware* and *NMI* noise 68 + occurrence observed by the tool. 69 + 70 + For example, *CPU 3* ran *599* periods of *1 second Runtime*. The CPU received 71 + *86 us* of noise during the entire execution, leaving *99.99997 %* of CPU time 72 + for the application. In the worst single period, the CPU caused *4 us* of 73 + noise to the application, but it was certainly caused by more than one single 74 + noise, as the *Max Single* noise was of *3 us*. The CPU has *HW noise,* at a 75 + rate of *six occurrences*/*ten minutes*. The CPU also has *NMIs*, at a higher 76 + frequency: around *seven per second*. 77 + 78 + The tool should report *0* hardware-related noise in the ideal situation. 79 + For example, by disabling hyper-threading to remove the hardware noise, 80 + and disabling the TSC watchdog to remove the NMI (it is possible to identify 81 + this using tracing options of **rtla hwnoise**), it was possible to reach 82 + the ideal situation in the same hardware:: 83 + 84 + # rtla hwnoise -c 1-7 -T 1 -d 10m -q 85 + Hardware-related Noise 86 + duration: 0 00:10:00 | time is in us 87 + CPU Period Runtime Noise % CPU Aval Max Noise Max Single HW NMI 88 + 1 #599 599000000 0 100.00000 0 0 0 0 89 + 2 #599 599000000 0 100.00000 0 0 0 0 90 + 3 #599 599000000 0 100.00000 0 0 0 0 91 + 4 #599 599000000 0 100.00000 0 0 0 0 92 + 5 #599 599000000 0 100.00000 0 0 0 0 93 + 6 #599 599000000 0 100.00000 0 0 0 0 94 + 7 #599 599000000 0 100.00000 0 0 0 0 95 + 96 + SEE ALSO 97 + ======== 98 + 99 + **rtla-osnoise**\(1) 100 + 101 + Osnoise tracer documentation: <https://www.kernel.org/doc/html/latest/trace/osnoise-tracer.html> 102 + 103 + AUTHOR 104 + ====== 105 + Written by Daniel Bristot de Oliveira <bristot@kernel.org> 106 + 107 + .. include:: common_appendix.rst
+71 -89
Documentation/tools/rtla/rtla-timerlat-top.rst
··· 30 30 31 31 .. include:: common_options.rst 32 32 33 + .. include:: common_timerlat_aa.rst 34 + 33 35 EXAMPLE 34 36 ======= 35 37 36 - In the example below, the *timerlat* tracer is set to capture the stack trace at 37 - the IRQ handler, printing it to the buffer if the *Thread* timer latency is 38 - higher than *30 us*. It is also set to stop the session if a *Thread* timer 39 - latency higher than *30 us* is hit. Finally, it is set to save the trace 40 - buffer if the stop condition is hit:: 38 + In the example below, the timerlat tracer is dispatched in cpus *1-23* in the 39 + automatic trace mode, instructing the tracer to stop if a *40 us* latency or 40 + higher is found:: 41 41 42 - [root@alien ~]# rtla timerlat top -s 30 -T 30 -t 43 - Timer Latency 44 - 0 00:00:59 | IRQ Timer Latency (us) | Thread Timer Latency (us) 42 + # timerlat -a 40 -c 1-23 -q 43 + Timer Latency 44 + 0 00:00:12 | IRQ Timer Latency (us) | Thread Timer Latency (us) 45 45 CPU COUNT | cur min avg max | cur min avg max 46 - 0 #58634 | 1 0 1 10 | 11 2 10 23 47 - 1 #58634 | 1 0 1 9 | 12 2 9 23 48 - 2 #58634 | 0 0 1 11 | 10 2 9 23 49 - 3 #58634 | 1 0 1 11 | 11 2 9 24 50 - 4 #58634 | 1 0 1 10 | 11 2 9 26 51 - 5 #58634 | 1 0 1 8 | 10 2 9 25 52 - 6 #58634 | 12 0 1 12 | 30 2 10 30 <--- CPU with spike 53 - 7 #58634 | 1 0 1 9 | 11 2 9 23 54 - 8 #58633 | 1 0 1 9 | 11 2 9 26 55 - 9 #58633 | 1 0 1 9 | 10 2 9 26 56 - 10 #58633 | 1 0 1 13 | 11 2 9 28 57 - 11 #58633 | 1 0 1 13 | 12 2 9 24 58 - 12 #58633 | 1 0 1 8 | 10 2 9 23 59 - 13 #58633 | 1 0 1 10 | 10 2 9 22 60 - 14 #58633 | 1 0 1 18 | 12 2 9 27 61 - 15 #58633 | 1 0 1 10 | 11 2 9 28 62 - 16 #58633 | 0 0 1 11 | 7 2 9 26 63 - 17 #58633 | 1 0 1 13 | 10 2 9 24 64 - 18 #58633 | 1 0 1 9 | 13 2 9 22 65 - 19 #58633 | 1 0 1 10 | 11 2 9 23 66 - 20 #58633 | 1 0 1 12 | 11 2 9 28 67 - 21 #58633 | 1 0 1 14 | 11 2 9 24 68 - 22 #58633 | 1 0 1 8 | 11 2 9 22 69 - 23 #58633 | 1 0 1 10 | 11 2 9 27 70 - timerlat hit stop tracing 71 - saving trace to timerlat_trace.txt 72 - [root@alien bristot]# tail -60 timerlat_trace.txt 73 - [...] 74 - timerlat/5-79755 [005] ....... 426.271226: #58634 context thread timer_latency 10823 ns 75 - sh-109404 [006] dnLh213 426.271247: #58634 context irq timer_latency 12505 ns 76 - sh-109404 [006] dNLh313 426.271258: irq_noise: local_timer:236 start 426.271245463 duration 12553 ns 77 - sh-109404 [006] d...313 426.271263: thread_noise: sh:109404 start 426.271245853 duration 4769 ns 78 - timerlat/6-79756 [006] ....... 426.271264: #58634 context thread timer_latency 30328 ns 79 - timerlat/6-79756 [006] ....1.. 426.271265: <stack trace> 80 - => timerlat_irq 81 - => __hrtimer_run_queues 82 - => hrtimer_interrupt 83 - => __sysvec_apic_timer_interrupt 84 - => sysvec_apic_timer_interrupt 85 - => asm_sysvec_apic_timer_interrupt 86 - => _raw_spin_unlock_irqrestore <---- spinlock that disabled interrupt. 87 - => try_to_wake_up 88 - => autoremove_wake_function 89 - => __wake_up_common 90 - => __wake_up_common_lock 91 - => ep_poll_callback 92 - => __wake_up_common 93 - => __wake_up_common_lock 94 - => fsnotify_add_event 95 - => inotify_handle_inode_event 96 - => fsnotify 97 - => __fsnotify_parent 98 - => __fput 99 - => task_work_run 100 - => exit_to_user_mode_prepare 101 - => syscall_exit_to_user_mode 102 - => do_syscall_64 103 - => entry_SYSCALL_64_after_hwframe 104 - => 0x7265000001378c 105 - => 0x10000cea7 106 - => 0x25a00000204a 107 - => 0x12e302d00000000 108 - => 0x19b51010901b6 109 - => 0x283ce00726500 110 - => 0x61ea308872 111 - => 0x00000fe3 112 - bash-109109 [007] d..h... 426.271265: #58634 context irq timer_latency 1211 ns 113 - timerlat/6-79756 [006] ....... 426.271267: timerlat_main: stop tracing hit on cpu 6 46 + 1 #12322 | 0 0 1 15 | 10 3 9 31 47 + 2 #12322 | 3 0 1 12 | 10 3 9 23 48 + 3 #12322 | 1 0 1 21 | 8 2 8 34 49 + 4 #12322 | 1 0 1 17 | 10 2 11 33 50 + 5 #12322 | 0 0 1 12 | 8 3 8 25 51 + 6 #12322 | 1 0 1 14 | 16 3 11 35 52 + 7 #12322 | 0 0 1 14 | 9 2 8 29 53 + 8 #12322 | 1 0 1 22 | 9 3 9 34 54 + 9 #12322 | 0 0 1 14 | 8 2 8 24 55 + 10 #12322 | 1 0 0 12 | 9 3 8 24 56 + 11 #12322 | 0 0 0 15 | 6 2 7 29 57 + 12 #12321 | 1 0 0 13 | 5 3 8 23 58 + 13 #12319 | 0 0 1 14 | 9 3 9 26 59 + 14 #12321 | 1 0 0 13 | 6 2 8 24 60 + 15 #12321 | 1 0 1 15 | 12 3 11 27 61 + 16 #12318 | 0 0 1 13 | 7 3 10 24 62 + 17 #12319 | 0 0 1 13 | 11 3 9 25 63 + 18 #12318 | 0 0 0 12 | 8 2 8 20 64 + 19 #12319 | 0 0 1 18 | 10 2 9 28 65 + 20 #12317 | 0 0 0 20 | 9 3 8 34 66 + 21 #12318 | 0 0 0 13 | 8 3 8 28 67 + 22 #12319 | 0 0 1 11 | 8 3 10 22 68 + 23 #12320 | 28 0 1 28 | 41 3 11 41 69 + rtla timerlat hit stop tracing 70 + ## CPU 23 hit stop tracing, analyzing it ## 71 + IRQ handler delay: 27.49 us (65.52 %) 72 + IRQ latency: 28.13 us 73 + Timerlat IRQ duration: 9.59 us (22.85 %) 74 + Blocking thread: 3.79 us (9.03 %) 75 + objtool:49256 3.79 us 76 + Blocking thread stacktrace 77 + -> timerlat_irq 78 + -> __hrtimer_run_queues 79 + -> hrtimer_interrupt 80 + -> __sysvec_apic_timer_interrupt 81 + -> sysvec_apic_timer_interrupt 82 + -> asm_sysvec_apic_timer_interrupt 83 + -> _raw_spin_unlock_irqrestore 84 + -> cgroup_rstat_flush_locked 85 + -> cgroup_rstat_flush_irqsafe 86 + -> mem_cgroup_flush_stats 87 + -> mem_cgroup_wb_stats 88 + -> balance_dirty_pages 89 + -> balance_dirty_pages_ratelimited_flags 90 + -> btrfs_buffered_write 91 + -> btrfs_do_write_iter 92 + -> vfs_write 93 + -> __x64_sys_pwrite64 94 + -> do_syscall_64 95 + -> entry_SYSCALL_64_after_hwframe 96 + ------------------------------------------------------------------------ 97 + Thread latency: 41.96 us (100%) 114 98 115 - In the trace, it is possible the notice that the *IRQ* timer latency was 116 - already high, accounting *12505 ns*. The IRQ delay was caused by the 117 - *bash-109109* process that disabled IRQs in the wake-up path 118 - (*_try_to_wake_up()* function). The duration of the IRQ handler that woke 119 - up the timerlat thread, informed with the **osnoise:irq_noise** event, was 120 - also high and added more *12553 ns* to the Thread latency. Finally, the 121 - **osnoise:thread_noise** added by the currently running thread (including 122 - the scheduling overhead) added more *4769 ns*. Summing up these values, 123 - the *Thread* timer latency accounted for *30328 ns*. 99 + The system has exit from idle latency! 100 + Max timerlat IRQ latency from idle: 17.48 us in cpu 4 101 + Saving trace to timerlat_trace.txt 124 102 125 - The primary reason for this high value is the wake-up path that was hit 126 - twice during this case: when the *bash-109109* was waking up a thread 127 - and then when the *timerlat* thread was awakened. This information can 128 - then be used as the starting point of a more fine-grained analysis. 103 + In this case, the major factor was the delay suffered by the *IRQ handler* 104 + that handles **timerlat** wakeup: *65.52%*. This can be caused by the 105 + current thread masking interrupts, which can be seen in the blocking 106 + thread stacktrace: the current thread (*objtool:49256*) disabled interrupts 107 + via *raw spin lock* operations inside mem cgroup, while doing write 108 + syscall in a btrfs file system. 109 + 110 + The raw trace is saved in the **timerlat_trace.txt** file for further analysis. 129 111 130 112 Note that **rtla timerlat** was dispatched without changing *timerlat* tracer 131 113 threads' priority. That is generally not needed because these threads hava
+2
tools/tracing/rtla/Makefile
··· 119 119 $(STRIP) $(DESTDIR)$(BINDIR)/rtla 120 120 @test ! -f $(DESTDIR)$(BINDIR)/osnoise || rm $(DESTDIR)$(BINDIR)/osnoise 121 121 ln -s rtla $(DESTDIR)$(BINDIR)/osnoise 122 + @test ! -f $(DESTDIR)$(BINDIR)/hwnoise || rm $(DESTDIR)$(BINDIR)/hwnoise 123 + ln -s rtla $(DESTDIR)$(BINDIR)/hwnoise 122 124 @test ! -f $(DESTDIR)$(BINDIR)/timerlat || rm $(DESTDIR)$(BINDIR)/timerlat 123 125 ln -s rtla $(DESTDIR)$(BINDIR)/timerlat 124 126
+117
tools/tracing/rtla/src/osnoise.c
··· 734 734 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL; 735 735 } 736 736 737 + static int osnoise_options_get_option(char *option) 738 + { 739 + char *options = tracefs_instance_file_read(NULL, "osnoise/options", NULL); 740 + char no_option[128]; 741 + int retval = 0; 742 + char *opt; 743 + 744 + if (!options) 745 + return OSNOISE_OPTION_INIT_VAL; 746 + 747 + /* 748 + * Check first if the option is disabled. 749 + */ 750 + snprintf(no_option, sizeof(no_option), "NO_%s", option); 751 + 752 + opt = strstr(options, no_option); 753 + if (opt) 754 + goto out_free; 755 + 756 + /* 757 + * Now that it is not disabled, if the string is there, it is 758 + * enabled. If the string is not there, the option does not exist. 759 + */ 760 + opt = strstr(options, option); 761 + if (opt) 762 + retval = 1; 763 + else 764 + retval = OSNOISE_OPTION_INIT_VAL; 765 + 766 + out_free: 767 + free(options); 768 + return retval; 769 + } 770 + 771 + static int osnoise_options_set_option(char *option, bool onoff) 772 + { 773 + char no_option[128]; 774 + 775 + if (onoff) 776 + return tracefs_instance_file_write(NULL, "osnoise/options", option); 777 + 778 + snprintf(no_option, sizeof(no_option), "NO_%s", option); 779 + 780 + return tracefs_instance_file_write(NULL, "osnoise/options", no_option); 781 + } 782 + 783 + static int osnoise_get_irq_disable(struct osnoise_context *context) 784 + { 785 + if (context->opt_irq_disable != OSNOISE_OPTION_INIT_VAL) 786 + return context->opt_irq_disable; 787 + 788 + if (context->orig_opt_irq_disable != OSNOISE_OPTION_INIT_VAL) 789 + return context->orig_opt_irq_disable; 790 + 791 + context->orig_opt_irq_disable = osnoise_options_get_option("OSNOISE_IRQ_DISABLE"); 792 + 793 + return context->orig_opt_irq_disable; 794 + } 795 + 796 + int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff) 797 + { 798 + int opt_irq_disable = osnoise_get_irq_disable(context); 799 + int retval; 800 + 801 + if (opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 802 + return -1; 803 + 804 + if (opt_irq_disable == onoff) 805 + return 0; 806 + 807 + retval = osnoise_options_set_option("OSNOISE_IRQ_DISABLE", onoff); 808 + if (retval < 0) 809 + return -1; 810 + 811 + context->opt_irq_disable = onoff; 812 + 813 + return 0; 814 + } 815 + 816 + static void osnoise_restore_irq_disable(struct osnoise_context *context) 817 + { 818 + int retval; 819 + 820 + if (context->orig_opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 821 + return; 822 + 823 + if (context->orig_opt_irq_disable == context->opt_irq_disable) 824 + goto out_done; 825 + 826 + retval = osnoise_options_set_option("OSNOISE_IRQ_DISABLE", context->orig_opt_irq_disable); 827 + if (retval < 0) 828 + err_msg("Could not restore original OSNOISE_IRQ_DISABLE option\n"); 829 + 830 + out_done: 831 + context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 832 + } 833 + 834 + static void osnoise_put_irq_disable(struct osnoise_context *context) 835 + { 836 + osnoise_restore_irq_disable(context); 837 + 838 + if (context->orig_opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 839 + return; 840 + 841 + context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 842 + } 843 + 737 844 /* 738 845 * enable_osnoise - enable osnoise tracer in the trace_instance 739 846 */ ··· 905 798 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL; 906 799 context->tracing_thresh = OSNOISE_OPTION_INIT_VAL; 907 800 801 + context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 802 + context->opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 803 + 908 804 osnoise_get_context(context); 909 805 910 806 return context; ··· 934 824 osnoise_put_timerlat_period_us(context); 935 825 osnoise_put_print_stack(context); 936 826 osnoise_put_tracing_thresh(context); 827 + osnoise_put_irq_disable(context); 937 828 938 829 free(context); 939 830 } ··· 1068 957 usage: 1069 958 osnoise_usage(1); 1070 959 exit(1); 960 + } 961 + 962 + int hwnoise_main(int argc, char *argv[]) 963 + { 964 + osnoise_top_main(argc, argv); 965 + exit(0); 1071 966 }
+7
tools/tracing/rtla/src/osnoise.h
··· 38 38 /* -1 as init value because 0 is disabled */ 39 39 long long orig_print_stack; 40 40 long long print_stack; 41 + 42 + /* -1 as init value because 0 is off */ 43 + int orig_opt_irq_disable; 44 + int opt_irq_disable; 41 45 }; 42 46 43 47 /* ··· 83 79 int osnoise_set_print_stack(struct osnoise_context *context, 84 80 long long print_stack); 85 81 82 + int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff); 83 + 86 84 /* 87 85 * osnoise_tool - osnoise based tool definition. 88 86 */ ··· 103 97 int osnoise_hist_main(int argc, char *argv[]); 104 98 int osnoise_top_main(int argc, char **argv); 105 99 int osnoise_main(int argc, char **argv); 100 + int hwnoise_main(int argc, char **argv);
+6 -3
tools/tracing/rtla/src/osnoise_hist.c
··· 121 121 { 122 122 struct osnoise_hist_params *params = tool->params; 123 123 struct osnoise_hist_data *data = tool->data; 124 + unsigned long long total_duration; 124 125 int entries = data->entries; 125 126 int bucket; 126 127 int *hist; ··· 132 131 if (data->bucket_size) 133 132 bucket = duration / data->bucket_size; 134 133 134 + total_duration = duration * count; 135 + 135 136 hist = data->hist[cpu].samples; 136 137 data->hist[cpu].count += count; 137 138 update_min(&data->hist[cpu].min_sample, &duration); 138 - update_sum(&data->hist[cpu].sum_sample, &duration); 139 + update_sum(&data->hist[cpu].sum_sample, &total_duration); 139 140 update_max(&data->hist[cpu].max_sample, &duration); 140 141 141 142 if (bucket < entries) ··· 335 332 continue; 336 333 337 334 if (data->hist[cpu].count) 338 - trace_seq_printf(trace->seq, "%9llu ", 339 - data->hist[cpu].sum_sample / data->hist[cpu].count); 335 + trace_seq_printf(trace->seq, "%9.2f ", 336 + ((double) data->hist[cpu].sum_sample) / data->hist[cpu].count); 340 337 else 341 338 trace_seq_printf(trace->seq, " - "); 342 339 }
+68 -16
tools/tracing/rtla/src/osnoise_top.c
··· 14 14 #include "osnoise.h" 15 15 #include "utils.h" 16 16 17 + enum osnoise_mode { 18 + MODE_OSNOISE = 0, 19 + MODE_HWNOISE 20 + }; 21 + 17 22 /* 18 23 * osnoise top parameters 19 24 */ ··· 37 32 int set_sched; 38 33 struct sched_attr sched_param; 39 34 struct trace_events *events; 35 + enum osnoise_mode mode; 40 36 }; 41 37 42 38 struct osnoise_top_cpu { ··· 149 143 */ 150 144 static void osnoise_top_header(struct osnoise_tool *top) 151 145 { 146 + struct osnoise_top_params *params = top->params; 152 147 struct trace_seq *s = top->trace.seq; 153 148 char duration[26]; 154 149 155 150 get_duration(top->start_time, duration, sizeof(duration)); 156 151 157 152 trace_seq_printf(s, "\033[2;37;40m"); 158 - trace_seq_printf(s, " Operating System Noise"); 159 - trace_seq_printf(s, " "); 160 - trace_seq_printf(s, " "); 153 + trace_seq_printf(s, " "); 154 + 155 + if (params->mode == MODE_OSNOISE) { 156 + trace_seq_printf(s, "Operating System Noise"); 157 + trace_seq_printf(s, " "); 158 + } else if (params->mode == MODE_HWNOISE) { 159 + trace_seq_printf(s, "Hardware-related Noise"); 160 + } 161 + 162 + trace_seq_printf(s, " "); 161 163 trace_seq_printf(s, "\033[0;0;0m"); 162 164 trace_seq_printf(s, "\n"); 163 165 ··· 176 162 trace_seq_printf(s, " Noise "); 177 163 trace_seq_printf(s, " %% CPU Aval "); 178 164 trace_seq_printf(s, " Max Noise Max Single "); 179 - trace_seq_printf(s, " HW NMI IRQ Softirq Thread"); 165 + trace_seq_printf(s, " HW NMI"); 166 + 167 + if (params->mode == MODE_HWNOISE) 168 + goto eol; 169 + 170 + trace_seq_printf(s, " IRQ Softirq Thread"); 171 + 172 + eol: 180 173 trace_seq_printf(s, "\033[0;0;0m"); 181 174 trace_seq_printf(s, "\n"); 182 175 } ··· 202 181 */ 203 182 static void osnoise_top_print(struct osnoise_tool *tool, int cpu) 204 183 { 184 + struct osnoise_top_params *params = tool->params; 205 185 struct trace_seq *s = tool->trace.seq; 206 186 struct osnoise_top_cpu *cpu_data; 207 187 struct osnoise_top_data *data; ··· 227 205 228 206 trace_seq_printf(s, "%12llu ", cpu_data->hw_count); 229 207 trace_seq_printf(s, "%12llu ", cpu_data->nmi_count); 208 + 209 + if (params->mode == MODE_HWNOISE) { 210 + trace_seq_printf(s, "\n"); 211 + return; 212 + } 213 + 230 214 trace_seq_printf(s, "%12llu ", cpu_data->irq_count); 231 215 trace_seq_printf(s, "%12llu ", cpu_data->softirq_count); 232 216 trace_seq_printf(s, "%12llu\n", cpu_data->thread_count); ··· 269 241 /* 270 242 * osnoise_top_usage - prints osnoise top usage message 271 243 */ 272 - void osnoise_top_usage(char *usage) 244 + static void osnoise_top_usage(struct osnoise_top_params *params, char *usage) 273 245 { 274 246 int i; 275 247 276 248 static const char * const msg[] = { 277 - " usage: rtla osnoise [top] [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", 249 + " [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", 278 250 " [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", 279 251 " [-c cpu-list] [-P priority]", 280 252 "", ··· 305 277 if (usage) 306 278 fprintf(stderr, "%s\n", usage); 307 279 308 - fprintf(stderr, "rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n", 280 + if (params->mode == MODE_OSNOISE) { 281 + fprintf(stderr, 282 + "rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n", 309 283 VERSION); 284 + 285 + fprintf(stderr, " usage: rtla osnoise [top]"); 286 + } 287 + 288 + if (params->mode == MODE_HWNOISE) { 289 + fprintf(stderr, 290 + "rtla hwnoise: a summary of hardware-related noise (version %s)\n", 291 + VERSION); 292 + 293 + fprintf(stderr, " usage: rtla hwnoise"); 294 + } 310 295 311 296 for (i = 0; msg[i]; i++) 312 297 fprintf(stderr, "%s\n", msg[i]); ··· 339 298 params = calloc(1, sizeof(*params)); 340 299 if (!params) 341 300 exit(1); 301 + 302 + if (strcmp(argv[0], "hwnoise") == 0) 303 + params->mode = MODE_HWNOISE; 342 304 343 305 while (1) { 344 306 static struct option long_options[] = { ··· 389 345 case 'c': 390 346 retval = parse_cpu_list(optarg, &params->monitored_cpus); 391 347 if (retval) 392 - osnoise_top_usage("\nInvalid -c cpu list\n"); 348 + osnoise_top_usage(params, "\nInvalid -c cpu list\n"); 393 349 params->cpus = optarg; 394 350 break; 395 351 case 'D': ··· 398 354 case 'd': 399 355 params->duration = parse_seconds_duration(optarg); 400 356 if (!params->duration) 401 - osnoise_top_usage("Invalid -D duration\n"); 357 + osnoise_top_usage(params, "Invalid -D duration\n"); 402 358 break; 403 359 case 'e': 404 360 tevent = trace_event_alloc(optarg); ··· 414 370 break; 415 371 case 'h': 416 372 case '?': 417 - osnoise_top_usage(NULL); 373 + osnoise_top_usage(params, NULL); 418 374 break; 419 375 case 'p': 420 376 params->period = get_llong_from_str(optarg); 421 377 if (params->period > 10000000) 422 - osnoise_top_usage("Period longer than 10 s\n"); 378 + osnoise_top_usage(params, "Period longer than 10 s\n"); 423 379 break; 424 380 case 'P': 425 381 retval = parse_prio(optarg, &params->sched_param); 426 382 if (retval == -1) 427 - osnoise_top_usage("Invalid -P priority"); 383 + osnoise_top_usage(params, "Invalid -P priority"); 428 384 params->set_sched = 1; 429 385 break; 430 386 case 'q': ··· 433 389 case 'r': 434 390 params->runtime = get_llong_from_str(optarg); 435 391 if (params->runtime < 100) 436 - osnoise_top_usage("Runtime shorter than 100 us\n"); 392 + osnoise_top_usage(params, "Runtime shorter than 100 us\n"); 437 393 break; 438 394 case 's': 439 395 params->stop_us = get_llong_from_str(optarg); ··· 459 415 exit(EXIT_FAILURE); 460 416 } 461 417 } else { 462 - osnoise_top_usage("--trigger requires a previous -e\n"); 418 + osnoise_top_usage(params, "--trigger requires a previous -e\n"); 463 419 } 464 420 break; 465 421 case '1': /* filter */ ··· 470 426 exit(EXIT_FAILURE); 471 427 } 472 428 } else { 473 - osnoise_top_usage("--filter requires a previous -e\n"); 429 + osnoise_top_usage(params, "--filter requires a previous -e\n"); 474 430 } 475 431 break; 476 432 default: 477 - osnoise_top_usage("Invalid option"); 433 + osnoise_top_usage(params, "Invalid option"); 478 434 } 479 435 } 480 436 ··· 535 491 retval = osnoise_set_tracing_thresh(tool->context, params->threshold); 536 492 if (retval) { 537 493 err_msg("Failed to set tracing_thresh\n"); 494 + goto out_err; 495 + } 496 + } 497 + 498 + if (params->mode == MODE_HWNOISE) { 499 + retval = osnoise_set_irq_disable(tool->context, 1); 500 + if (retval) { 501 + err_msg("Failed to set OSNOISE_IRQ_DISABLE option\n"); 538 502 goto out_err; 539 503 } 540 504 }
+4
tools/tracing/rtla/src/rtla.c
··· 26 26 "", 27 27 " commands:", 28 28 " osnoise - gives information about the operating system noise (osnoise)", 29 + " hwnoise - gives information about hardware-related noise", 29 30 " timerlat - measures the timer irq and thread latency", 30 31 "", 31 32 NULL, ··· 47 46 { 48 47 if (strcmp(argv[start_position], "osnoise") == 0) { 49 48 osnoise_main(argc-start_position, &argv[start_position]); 49 + goto ran; 50 + } else if (strcmp(argv[start_position], "hwnoise") == 0) { 51 + hwnoise_main(argc-start_position, &argv[start_position]); 50 52 goto ran; 51 53 } else if (strcmp(argv[start_position], "timerlat") == 0) { 52 54 timerlat_main(argc-start_position, &argv[start_position]);
+990
tools/tracing/rtla/src/timerlat_aa.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> 4 + */ 5 + 6 + #include <stdlib.h> 7 + #include <errno.h> 8 + #include "utils.h" 9 + #include "osnoise.h" 10 + #include "timerlat.h" 11 + 12 + enum timelat_state { 13 + TIMERLAT_INIT = 0, 14 + TIMERLAT_WAITING_IRQ, 15 + TIMERLAT_WAITING_THREAD, 16 + }; 17 + 18 + #define MAX_COMM 24 19 + 20 + /* 21 + * Per-cpu data statistics and data. 22 + */ 23 + struct timerlat_aa_data { 24 + /* Current CPU state */ 25 + int curr_state; 26 + 27 + /* timerlat IRQ latency */ 28 + unsigned long long tlat_irq_seqnum; 29 + unsigned long long tlat_irq_latency; 30 + unsigned long long tlat_irq_timstamp; 31 + 32 + /* timerlat Thread latency */ 33 + unsigned long long tlat_thread_seqnum; 34 + unsigned long long tlat_thread_latency; 35 + unsigned long long tlat_thread_timstamp; 36 + 37 + /* 38 + * Information about the thread running when the IRQ 39 + * arrived. 40 + * 41 + * This can be blocking or interference, depending on the 42 + * priority of the thread. Assuming timerlat is the highest 43 + * prio, it is blocking. If timerlat has a lower prio, it is 44 + * interference. 45 + * note: "unsigned long long" because they are fetch using tep_get_field_val(); 46 + */ 47 + unsigned long long run_thread_pid; 48 + char run_thread_comm[MAX_COMM]; 49 + unsigned long long thread_blocking_duration; 50 + unsigned long long max_exit_idle_latency; 51 + 52 + /* Information about the timerlat timer irq */ 53 + unsigned long long timer_irq_start_time; 54 + unsigned long long timer_irq_start_delay; 55 + unsigned long long timer_irq_duration; 56 + unsigned long long timer_exit_from_idle; 57 + 58 + /* 59 + * Information about the last IRQ before the timerlat irq 60 + * arrived. 61 + * 62 + * If now - timestamp is <= latency, it might have influenced 63 + * in the timerlat irq latency. Otherwise, ignore it. 64 + */ 65 + unsigned long long prev_irq_duration; 66 + unsigned long long prev_irq_timstamp; 67 + 68 + /* 69 + * Interference sum. 70 + */ 71 + unsigned long long thread_nmi_sum; 72 + unsigned long long thread_irq_sum; 73 + unsigned long long thread_softirq_sum; 74 + unsigned long long thread_thread_sum; 75 + 76 + /* 77 + * Interference task information. 78 + */ 79 + struct trace_seq *prev_irqs_seq; 80 + struct trace_seq *nmi_seq; 81 + struct trace_seq *irqs_seq; 82 + struct trace_seq *softirqs_seq; 83 + struct trace_seq *threads_seq; 84 + struct trace_seq *stack_seq; 85 + 86 + /* 87 + * Current thread. 88 + */ 89 + char current_comm[MAX_COMM]; 90 + unsigned long long current_pid; 91 + 92 + /* 93 + * Is the system running a kworker? 94 + */ 95 + unsigned long long kworker; 96 + unsigned long long kworker_func; 97 + }; 98 + 99 + /* 100 + * The analysis context and system wide view 101 + */ 102 + struct timerlat_aa_context { 103 + int nr_cpus; 104 + int dump_tasks; 105 + 106 + /* per CPU data */ 107 + struct timerlat_aa_data *taa_data; 108 + 109 + /* 110 + * required to translate function names and register 111 + * events. 112 + */ 113 + struct osnoise_tool *tool; 114 + }; 115 + 116 + /* 117 + * The data is stored as a local variable, but accessed via a helper function. 118 + * 119 + * It could be stored inside the trace context. But every access would 120 + * require container_of() + a series of pointers. Do we need it? Not sure. 121 + * 122 + * For now keep it simple. If needed, store it in the tool, add the *context 123 + * as a parameter in timerlat_aa_get_ctx() and do the magic there. 124 + */ 125 + static struct timerlat_aa_context *__timerlat_aa_ctx; 126 + 127 + static struct timerlat_aa_context *timerlat_aa_get_ctx(void) 128 + { 129 + return __timerlat_aa_ctx; 130 + } 131 + 132 + /* 133 + * timerlat_aa_get_data - Get the per-cpu data from the timerlat context 134 + */ 135 + static struct timerlat_aa_data 136 + *timerlat_aa_get_data(struct timerlat_aa_context *taa_ctx, int cpu) 137 + { 138 + return &taa_ctx->taa_data[cpu]; 139 + } 140 + 141 + /* 142 + * timerlat_aa_irq_latency - Handles timerlat IRQ event 143 + */ 144 + static int timerlat_aa_irq_latency(struct timerlat_aa_data *taa_data, 145 + struct trace_seq *s, struct tep_record *record, 146 + struct tep_event *event) 147 + { 148 + /* 149 + * For interference, we start now looking for things that can delay 150 + * the thread. 151 + */ 152 + taa_data->curr_state = TIMERLAT_WAITING_THREAD; 153 + taa_data->tlat_irq_timstamp = record->ts; 154 + 155 + /* 156 + * Zero values. 157 + */ 158 + taa_data->thread_nmi_sum = 0; 159 + taa_data->thread_irq_sum = 0; 160 + taa_data->thread_softirq_sum = 0; 161 + taa_data->thread_blocking_duration = 0; 162 + taa_data->timer_irq_start_time = 0; 163 + taa_data->timer_irq_duration = 0; 164 + taa_data->timer_exit_from_idle = 0; 165 + 166 + /* 167 + * Zero interference tasks. 168 + */ 169 + trace_seq_reset(taa_data->nmi_seq); 170 + trace_seq_reset(taa_data->irqs_seq); 171 + trace_seq_reset(taa_data->softirqs_seq); 172 + trace_seq_reset(taa_data->threads_seq); 173 + 174 + /* IRQ latency values */ 175 + tep_get_field_val(s, event, "timer_latency", record, &taa_data->tlat_irq_latency, 1); 176 + tep_get_field_val(s, event, "seqnum", record, &taa_data->tlat_irq_seqnum, 1); 177 + 178 + /* The thread that can cause blocking */ 179 + tep_get_common_field_val(s, event, "common_pid", record, &taa_data->run_thread_pid, 1); 180 + 181 + /* 182 + * Get exit from idle case. 183 + * 184 + * If it is not idle thread: 185 + */ 186 + if (taa_data->run_thread_pid) 187 + return 0; 188 + 189 + /* 190 + * if the latency is shorter than the known exit from idle: 191 + */ 192 + if (taa_data->tlat_irq_latency < taa_data->max_exit_idle_latency) 193 + return 0; 194 + 195 + /* 196 + * To be safe, ignore the cases in which an IRQ/NMI could have 197 + * interfered with the timerlat IRQ. 198 + */ 199 + if (taa_data->tlat_irq_timstamp - taa_data->tlat_irq_latency 200 + < taa_data->prev_irq_timstamp + taa_data->prev_irq_duration) 201 + return 0; 202 + 203 + taa_data->max_exit_idle_latency = taa_data->tlat_irq_latency; 204 + 205 + return 0; 206 + } 207 + 208 + /* 209 + * timerlat_aa_thread_latency - Handles timerlat thread event 210 + */ 211 + static int timerlat_aa_thread_latency(struct timerlat_aa_data *taa_data, 212 + struct trace_seq *s, struct tep_record *record, 213 + struct tep_event *event) 214 + { 215 + /* 216 + * For interference, we start now looking for things that can delay 217 + * the IRQ of the next cycle. 218 + */ 219 + taa_data->curr_state = TIMERLAT_WAITING_IRQ; 220 + taa_data->tlat_thread_timstamp = record->ts; 221 + 222 + /* Thread latency values */ 223 + tep_get_field_val(s, event, "timer_latency", record, &taa_data->tlat_thread_latency, 1); 224 + tep_get_field_val(s, event, "seqnum", record, &taa_data->tlat_thread_seqnum, 1); 225 + 226 + return 0; 227 + } 228 + 229 + /* 230 + * timerlat_aa_handler - Handle timerlat events 231 + * 232 + * This function is called to handle timerlat events recording statistics. 233 + * 234 + * Returns 0 on success, -1 otherwise. 235 + */ 236 + int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record, 237 + struct tep_event *event, void *context) 238 + { 239 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 240 + struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); 241 + unsigned long long thread; 242 + 243 + if (!taa_data) 244 + return -1; 245 + 246 + tep_get_field_val(s, event, "context", record, &thread, 1); 247 + if (!thread) 248 + return timerlat_aa_irq_latency(taa_data, s, record, event); 249 + else 250 + return timerlat_aa_thread_latency(taa_data, s, record, event); 251 + } 252 + 253 + /* 254 + * timerlat_aa_nmi_handler - Handles NMI noise 255 + * 256 + * It is used to collect information about interferences from NMI. It is 257 + * hooked to the osnoise:nmi_noise event. 258 + */ 259 + static int timerlat_aa_nmi_handler(struct trace_seq *s, struct tep_record *record, 260 + struct tep_event *event, void *context) 261 + { 262 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 263 + struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); 264 + unsigned long long duration; 265 + unsigned long long start; 266 + 267 + tep_get_field_val(s, event, "duration", record, &duration, 1); 268 + tep_get_field_val(s, event, "start", record, &start, 1); 269 + 270 + if (taa_data->curr_state == TIMERLAT_WAITING_IRQ) { 271 + taa_data->prev_irq_duration = duration; 272 + taa_data->prev_irq_timstamp = start; 273 + 274 + trace_seq_reset(taa_data->prev_irqs_seq); 275 + trace_seq_printf(taa_data->prev_irqs_seq, "\t%24s \t\t\t%9.2f us\n", 276 + "nmi", ns_to_usf(duration)); 277 + return 0; 278 + } 279 + 280 + taa_data->thread_nmi_sum += duration; 281 + trace_seq_printf(taa_data->nmi_seq, " %24s \t\t\t%9.2f us\n", 282 + "nmi", ns_to_usf(duration)); 283 + 284 + return 0; 285 + } 286 + 287 + /* 288 + * timerlat_aa_irq_handler - Handles IRQ noise 289 + * 290 + * It is used to collect information about interferences from IRQ. It is 291 + * hooked to the osnoise:irq_noise event. 292 + * 293 + * It is a little bit more complex than the other because it measures: 294 + * - The IRQs that can delay the timer IRQ before it happened. 295 + * - The Timerlat IRQ handler 296 + * - The IRQs that happened between the timerlat IRQ and the timerlat thread 297 + * (IRQ interference). 298 + */ 299 + static int timerlat_aa_irq_handler(struct trace_seq *s, struct tep_record *record, 300 + struct tep_event *event, void *context) 301 + { 302 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 303 + struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); 304 + unsigned long long expected_start; 305 + unsigned long long duration; 306 + unsigned long long vector; 307 + unsigned long long start; 308 + char *desc; 309 + int val; 310 + 311 + tep_get_field_val(s, event, "duration", record, &duration, 1); 312 + tep_get_field_val(s, event, "start", record, &start, 1); 313 + tep_get_field_val(s, event, "vector", record, &vector, 1); 314 + desc = tep_get_field_raw(s, event, "desc", record, &val, 1); 315 + 316 + /* 317 + * Before the timerlat IRQ. 318 + */ 319 + if (taa_data->curr_state == TIMERLAT_WAITING_IRQ) { 320 + taa_data->prev_irq_duration = duration; 321 + taa_data->prev_irq_timstamp = start; 322 + 323 + trace_seq_reset(taa_data->prev_irqs_seq); 324 + trace_seq_printf(taa_data->prev_irqs_seq, "\t%24s:%-3llu \t\t%9.2f us\n", 325 + desc, vector, ns_to_usf(duration)); 326 + return 0; 327 + } 328 + 329 + /* 330 + * The timerlat IRQ: taa_data->timer_irq_start_time is zeroed at 331 + * the timerlat irq handler. 332 + */ 333 + if (!taa_data->timer_irq_start_time) { 334 + expected_start = taa_data->tlat_irq_timstamp - taa_data->tlat_irq_latency; 335 + 336 + taa_data->timer_irq_start_time = start; 337 + taa_data->timer_irq_duration = duration; 338 + 339 + taa_data->timer_irq_start_delay = taa_data->timer_irq_start_time - expected_start; 340 + 341 + /* 342 + * not exit from idle. 343 + */ 344 + if (taa_data->run_thread_pid) 345 + return 0; 346 + 347 + if (expected_start > taa_data->prev_irq_timstamp + taa_data->prev_irq_duration) 348 + taa_data->timer_exit_from_idle = taa_data->timer_irq_start_delay; 349 + 350 + return 0; 351 + } 352 + 353 + /* 354 + * IRQ interference. 355 + */ 356 + taa_data->thread_irq_sum += duration; 357 + trace_seq_printf(taa_data->irqs_seq, " %24s:%-3llu \t %9.2f us\n", 358 + desc, vector, ns_to_usf(duration)); 359 + 360 + return 0; 361 + } 362 + 363 + static char *softirq_name[] = { "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", 364 + "IRQ_POLL", "TASKLET", "SCHED", "HRTIMER", "RCU" }; 365 + 366 + 367 + /* 368 + * timerlat_aa_softirq_handler - Handles Softirq noise 369 + * 370 + * It is used to collect information about interferences from Softirq. It is 371 + * hooked to the osnoise:softirq_noise event. 372 + * 373 + * It is only printed in the non-rt kernel, as softirqs become thread on RT. 374 + */ 375 + static int timerlat_aa_softirq_handler(struct trace_seq *s, struct tep_record *record, 376 + struct tep_event *event, void *context) 377 + { 378 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 379 + struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); 380 + unsigned long long duration; 381 + unsigned long long vector; 382 + unsigned long long start; 383 + 384 + if (taa_data->curr_state == TIMERLAT_WAITING_IRQ) 385 + return 0; 386 + 387 + tep_get_field_val(s, event, "duration", record, &duration, 1); 388 + tep_get_field_val(s, event, "start", record, &start, 1); 389 + tep_get_field_val(s, event, "vector", record, &vector, 1); 390 + 391 + taa_data->thread_softirq_sum += duration; 392 + 393 + trace_seq_printf(taa_data->softirqs_seq, "\t%24s:%-3llu \t %9.2f us\n", 394 + softirq_name[vector], vector, ns_to_usf(duration)); 395 + return 0; 396 + } 397 + 398 + /* 399 + * timerlat_aa_softirq_handler - Handles thread noise 400 + * 401 + * It is used to collect information about interferences from threads. It is 402 + * hooked to the osnoise:thread_noise event. 403 + * 404 + * Note: if you see thread noise, your timerlat thread was not the highest prio one. 405 + */ 406 + static int timerlat_aa_thread_handler(struct trace_seq *s, struct tep_record *record, 407 + struct tep_event *event, void *context) 408 + { 409 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 410 + struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); 411 + unsigned long long duration; 412 + unsigned long long start; 413 + unsigned long long pid; 414 + const char *comm; 415 + int val; 416 + 417 + if (taa_data->curr_state == TIMERLAT_WAITING_IRQ) 418 + return 0; 419 + 420 + tep_get_field_val(s, event, "duration", record, &duration, 1); 421 + tep_get_field_val(s, event, "start", record, &start, 1); 422 + 423 + tep_get_common_field_val(s, event, "common_pid", record, &pid, 1); 424 + comm = tep_get_field_raw(s, event, "comm", record, &val, 1); 425 + 426 + if (pid == taa_data->run_thread_pid && !taa_data->thread_blocking_duration) { 427 + taa_data->thread_blocking_duration = duration; 428 + 429 + if (comm) 430 + strncpy(taa_data->run_thread_comm, comm, MAX_COMM); 431 + else 432 + sprintf(taa_data->run_thread_comm, "<...>"); 433 + 434 + } else { 435 + taa_data->thread_thread_sum += duration; 436 + 437 + trace_seq_printf(taa_data->threads_seq, "\t%24s:%-3llu \t\t%9.2f us\n", 438 + comm, pid, ns_to_usf(duration)); 439 + } 440 + 441 + return 0; 442 + } 443 + 444 + /* 445 + * timerlat_aa_stack_handler - Handles timerlat IRQ stack trace 446 + * 447 + * Saves and parse the stack trace generated by the timerlat IRQ. 448 + */ 449 + static int timerlat_aa_stack_handler(struct trace_seq *s, struct tep_record *record, 450 + struct tep_event *event, void *context) 451 + { 452 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 453 + struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); 454 + unsigned long *caller; 455 + const char *function; 456 + int val, i; 457 + 458 + trace_seq_reset(taa_data->stack_seq); 459 + 460 + trace_seq_printf(taa_data->stack_seq, " Blocking thread stack trace\n"); 461 + caller = tep_get_field_raw(s, event, "caller", record, &val, 1); 462 + if (caller) { 463 + for (i = 0; ; i++) { 464 + function = tep_find_function(taa_ctx->tool->trace.tep, caller[i]); 465 + if (!function) 466 + break; 467 + trace_seq_printf(taa_data->stack_seq, "\t\t-> %s\n", function); 468 + } 469 + } 470 + return 0; 471 + } 472 + 473 + /* 474 + * timerlat_aa_sched_switch_handler - Tracks the current thread running on the CPU 475 + * 476 + * Handles the sched:sched_switch event to trace the current thread running on the 477 + * CPU. It is used to display the threads running on the other CPUs when the trace 478 + * stops. 479 + */ 480 + static int timerlat_aa_sched_switch_handler(struct trace_seq *s, struct tep_record *record, 481 + struct tep_event *event, void *context) 482 + { 483 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 484 + struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); 485 + const char *comm; 486 + int val; 487 + 488 + tep_get_field_val(s, event, "next_pid", record, &taa_data->current_pid, 1); 489 + comm = tep_get_field_raw(s, event, "next_comm", record, &val, 1); 490 + 491 + strncpy(taa_data->current_comm, comm, MAX_COMM); 492 + 493 + /* 494 + * If this was a kworker, clean the last kworkers that ran. 495 + */ 496 + taa_data->kworker = 0; 497 + taa_data->kworker_func = 0; 498 + 499 + return 0; 500 + } 501 + 502 + /* 503 + * timerlat_aa_kworker_start_handler - Tracks a kworker running on the CPU 504 + * 505 + * Handles workqueue:workqueue_execute_start event, keeping track of 506 + * the job that a kworker could be doing in the CPU. 507 + * 508 + * We already catch problems of hardware related latencies caused by work queues 509 + * running driver code that causes hardware stall. For example, with DRM drivers. 510 + */ 511 + static int timerlat_aa_kworker_start_handler(struct trace_seq *s, struct tep_record *record, 512 + struct tep_event *event, void *context) 513 + { 514 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 515 + struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); 516 + 517 + tep_get_field_val(s, event, "work", record, &taa_data->kworker, 1); 518 + tep_get_field_val(s, event, "function", record, &taa_data->kworker_func, 1); 519 + return 0; 520 + } 521 + 522 + /* 523 + * timerlat_thread_analysis - Prints the analysis of a CPU that hit a stop tracing 524 + * 525 + * This is the core of the analysis. 526 + */ 527 + static void timerlat_thread_analysis(struct timerlat_aa_data *taa_data, int cpu, 528 + int irq_thresh, int thread_thresh) 529 + { 530 + unsigned long long exp_irq_ts; 531 + int total; 532 + int irq; 533 + 534 + /* 535 + * IRQ latency or Thread latency? 536 + */ 537 + if (taa_data->tlat_irq_seqnum > taa_data->tlat_thread_seqnum) { 538 + irq = 1; 539 + total = taa_data->tlat_irq_latency; 540 + } else { 541 + irq = 0; 542 + total = taa_data->tlat_thread_latency; 543 + } 544 + 545 + /* 546 + * Expected IRQ arrival time using the trace clock as the base. 547 + */ 548 + exp_irq_ts = taa_data->timer_irq_start_time - taa_data->timer_irq_start_delay; 549 + 550 + if (exp_irq_ts < taa_data->prev_irq_timstamp + taa_data->prev_irq_duration) 551 + printf(" Previous IRQ interference: \t up to %9.2f us", 552 + ns_to_usf(taa_data->prev_irq_duration)); 553 + 554 + /* 555 + * The delay that the IRQ suffered before starting. 556 + */ 557 + printf(" IRQ handler delay: %16s %9.2f us (%.2f %%)\n", 558 + (ns_to_usf(taa_data->timer_exit_from_idle) > 10) ? "(exit from idle)" : "", 559 + ns_to_usf(taa_data->timer_irq_start_delay), 560 + ns_to_per(total, taa_data->timer_irq_start_delay)); 561 + 562 + /* 563 + * Timerlat IRQ. 564 + */ 565 + printf(" IRQ latency: \t\t\t\t %9.2f us\n", 566 + ns_to_usf(taa_data->tlat_irq_latency)); 567 + 568 + if (irq) { 569 + /* 570 + * If the trace stopped due to IRQ, the other events will not happen 571 + * because... the trace stopped :-). 572 + * 573 + * That is all folks, the stack trace was printed before the stop, 574 + * so it will be displayed, it is the key. 575 + */ 576 + printf(" Blocking thread:\n"); 577 + printf(" %24s:%-9llu\n", 578 + taa_data->run_thread_comm, taa_data->run_thread_pid); 579 + } else { 580 + /* 581 + * The duration of the IRQ handler that handled the timerlat IRQ. 582 + */ 583 + printf(" Timerlat IRQ duration: \t\t %9.2f us (%.2f %%)\n", 584 + ns_to_usf(taa_data->timer_irq_duration), 585 + ns_to_per(total, taa_data->timer_irq_duration)); 586 + 587 + /* 588 + * The amount of time that the current thread postponed the scheduler. 589 + * 590 + * Recalling that it is net from NMI/IRQ/Softirq interference, so there 591 + * is no need to compute values here. 592 + */ 593 + printf(" Blocking thread: \t\t\t %9.2f us (%.2f %%)\n", 594 + ns_to_usf(taa_data->thread_blocking_duration), 595 + ns_to_per(total, taa_data->thread_blocking_duration)); 596 + 597 + printf(" %24s:%-9llu %9.2f us\n", 598 + taa_data->run_thread_comm, taa_data->run_thread_pid, 599 + ns_to_usf(taa_data->thread_blocking_duration)); 600 + } 601 + 602 + /* 603 + * Print the stack trace! 604 + */ 605 + trace_seq_do_printf(taa_data->stack_seq); 606 + 607 + /* 608 + * NMIs can happen during the IRQ, so they are always possible. 609 + */ 610 + if (taa_data->thread_nmi_sum) 611 + printf(" NMI interference \t\t\t %9.2f us (%.2f %%)\n", 612 + ns_to_usf(taa_data->thread_nmi_sum), 613 + ns_to_per(total, taa_data->thread_nmi_sum)); 614 + 615 + /* 616 + * If it is an IRQ latency, the other factors can be skipped. 617 + */ 618 + if (irq) 619 + goto print_total; 620 + 621 + /* 622 + * Prints the interference caused by IRQs to the thread latency. 623 + */ 624 + if (taa_data->thread_irq_sum) { 625 + printf(" IRQ interference \t\t\t %9.2f us (%.2f %%)\n", 626 + ns_to_usf(taa_data->thread_irq_sum), 627 + ns_to_per(total, taa_data->thread_irq_sum)); 628 + 629 + trace_seq_do_printf(taa_data->irqs_seq); 630 + } 631 + 632 + /* 633 + * Prints the interference caused by Softirqs to the thread latency. 634 + */ 635 + if (taa_data->thread_softirq_sum) { 636 + printf(" Softirq interference \t\t\t %9.2f us (%.2f %%)\n", 637 + ns_to_usf(taa_data->thread_softirq_sum), 638 + ns_to_per(total, taa_data->thread_softirq_sum)); 639 + 640 + trace_seq_do_printf(taa_data->softirqs_seq); 641 + } 642 + 643 + /* 644 + * Prints the interference caused by other threads to the thread latency. 645 + * 646 + * If this happens, your timerlat is not the highest prio. OK, migration 647 + * thread can happen. But otherwise, you are not measuring the "scheduling 648 + * latency" only, and here is the difference from scheduling latency and 649 + * timer handling latency. 650 + */ 651 + if (taa_data->thread_thread_sum) { 652 + printf(" Thread interference \t\t\t %9.2f us (%.2f %%)\n", 653 + ns_to_usf(taa_data->thread_thread_sum), 654 + ns_to_per(total, taa_data->thread_thread_sum)); 655 + 656 + trace_seq_do_printf(taa_data->threads_seq); 657 + } 658 + 659 + /* 660 + * Done. 661 + */ 662 + print_total: 663 + printf("------------------------------------------------------------------------\n"); 664 + printf(" %s latency: \t\t\t %9.2f us (100%%)\n", irq ? "IRQ" : "Thread", 665 + ns_to_usf(total)); 666 + } 667 + 668 + /** 669 + * timerlat_auto_analysis - Analyze the collected data 670 + */ 671 + void timerlat_auto_analysis(int irq_thresh, int thread_thresh) 672 + { 673 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 674 + unsigned long long max_exit_from_idle = 0; 675 + struct timerlat_aa_data *taa_data; 676 + int max_exit_from_idle_cpu; 677 + struct tep_handle *tep; 678 + int cpu; 679 + 680 + /* bring stop tracing to the ns scale */ 681 + irq_thresh = irq_thresh * 1000; 682 + thread_thresh = thread_thresh * 1000; 683 + 684 + for (cpu = 0; cpu < taa_ctx->nr_cpus; cpu++) { 685 + taa_data = timerlat_aa_get_data(taa_ctx, cpu); 686 + 687 + if (irq_thresh && taa_data->tlat_irq_latency >= irq_thresh) { 688 + printf("## CPU %d hit stop tracing, analyzing it ##\n", cpu); 689 + timerlat_thread_analysis(taa_data, cpu, irq_thresh, thread_thresh); 690 + } else if (thread_thresh && (taa_data->tlat_thread_latency) >= thread_thresh) { 691 + printf("## CPU %d hit stop tracing, analyzing it ##\n", cpu); 692 + timerlat_thread_analysis(taa_data, cpu, irq_thresh, thread_thresh); 693 + } 694 + 695 + if (taa_data->max_exit_idle_latency > max_exit_from_idle) { 696 + max_exit_from_idle = taa_data->max_exit_idle_latency; 697 + max_exit_from_idle_cpu = cpu; 698 + } 699 + 700 + } 701 + 702 + if (max_exit_from_idle) { 703 + printf("\n"); 704 + printf("Max timerlat IRQ latency from idle: %.2f us in cpu %d\n", 705 + ns_to_usf(max_exit_from_idle), max_exit_from_idle_cpu); 706 + } 707 + if (!taa_ctx->dump_tasks) 708 + return; 709 + 710 + printf("\n"); 711 + printf("Printing CPU tasks:\n"); 712 + for (cpu = 0; cpu < taa_ctx->nr_cpus; cpu++) { 713 + taa_data = timerlat_aa_get_data(taa_ctx, cpu); 714 + tep = taa_ctx->tool->trace.tep; 715 + 716 + printf(" [%.3d] %24s:%llu", cpu, taa_data->current_comm, taa_data->current_pid); 717 + 718 + if (taa_data->kworker_func) 719 + printf(" kworker:%s:%s", 720 + tep_find_function(tep, taa_data->kworker) ? : "<...>", 721 + tep_find_function(tep, taa_data->kworker_func)); 722 + printf("\n"); 723 + } 724 + 725 + } 726 + 727 + /* 728 + * timerlat_aa_destroy_seqs - Destroy seq files used to store parsed data 729 + */ 730 + static void timerlat_aa_destroy_seqs(struct timerlat_aa_context *taa_ctx) 731 + { 732 + struct timerlat_aa_data *taa_data; 733 + int i; 734 + 735 + if (!taa_ctx->taa_data) 736 + return; 737 + 738 + for (i = 0; i < taa_ctx->nr_cpus; i++) { 739 + taa_data = timerlat_aa_get_data(taa_ctx, i); 740 + 741 + if (taa_data->prev_irqs_seq) { 742 + trace_seq_destroy(taa_data->prev_irqs_seq); 743 + free(taa_data->prev_irqs_seq); 744 + } 745 + 746 + if (taa_data->nmi_seq) { 747 + trace_seq_destroy(taa_data->nmi_seq); 748 + free(taa_data->nmi_seq); 749 + } 750 + 751 + if (taa_data->irqs_seq) { 752 + trace_seq_destroy(taa_data->irqs_seq); 753 + free(taa_data->irqs_seq); 754 + } 755 + 756 + if (taa_data->softirqs_seq) { 757 + trace_seq_destroy(taa_data->softirqs_seq); 758 + free(taa_data->softirqs_seq); 759 + } 760 + 761 + if (taa_data->threads_seq) { 762 + trace_seq_destroy(taa_data->threads_seq); 763 + free(taa_data->threads_seq); 764 + } 765 + 766 + if (taa_data->stack_seq) { 767 + trace_seq_destroy(taa_data->stack_seq); 768 + free(taa_data->stack_seq); 769 + } 770 + } 771 + } 772 + 773 + /* 774 + * timerlat_aa_init_seqs - Init seq files used to store parsed information 775 + * 776 + * Instead of keeping data structures to store raw data, use seq files to 777 + * store parsed data. 778 + * 779 + * Allocates and initialize seq files. 780 + * 781 + * Returns 0 on success, -1 otherwise. 782 + */ 783 + static int timerlat_aa_init_seqs(struct timerlat_aa_context *taa_ctx) 784 + { 785 + struct timerlat_aa_data *taa_data; 786 + int i; 787 + 788 + for (i = 0; i < taa_ctx->nr_cpus; i++) { 789 + 790 + taa_data = timerlat_aa_get_data(taa_ctx, i); 791 + 792 + taa_data->prev_irqs_seq = calloc(1, sizeof(*taa_data->prev_irqs_seq)); 793 + if (!taa_data->prev_irqs_seq) 794 + goto out_err; 795 + 796 + trace_seq_init(taa_data->prev_irqs_seq); 797 + 798 + taa_data->nmi_seq = calloc(1, sizeof(*taa_data->nmi_seq)); 799 + if (!taa_data->nmi_seq) 800 + goto out_err; 801 + 802 + trace_seq_init(taa_data->nmi_seq); 803 + 804 + taa_data->irqs_seq = calloc(1, sizeof(*taa_data->irqs_seq)); 805 + if (!taa_data->irqs_seq) 806 + goto out_err; 807 + 808 + trace_seq_init(taa_data->irqs_seq); 809 + 810 + taa_data->softirqs_seq = calloc(1, sizeof(*taa_data->softirqs_seq)); 811 + if (!taa_data->softirqs_seq) 812 + goto out_err; 813 + 814 + trace_seq_init(taa_data->softirqs_seq); 815 + 816 + taa_data->threads_seq = calloc(1, sizeof(*taa_data->threads_seq)); 817 + if (!taa_data->threads_seq) 818 + goto out_err; 819 + 820 + trace_seq_init(taa_data->threads_seq); 821 + 822 + taa_data->stack_seq = calloc(1, sizeof(*taa_data->stack_seq)); 823 + if (!taa_data->stack_seq) 824 + goto out_err; 825 + 826 + trace_seq_init(taa_data->stack_seq); 827 + } 828 + 829 + return 0; 830 + 831 + out_err: 832 + timerlat_aa_destroy_seqs(taa_ctx); 833 + return -1; 834 + } 835 + 836 + /* 837 + * timerlat_aa_unregister_events - Unregister events used in the auto-analysis 838 + */ 839 + static void timerlat_aa_unregister_events(struct osnoise_tool *tool, int dump_tasks) 840 + { 841 + tracefs_event_disable(tool->trace.inst, "osnoise", NULL); 842 + 843 + tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "nmi_noise", 844 + timerlat_aa_nmi_handler, tool); 845 + 846 + tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "irq_noise", 847 + timerlat_aa_irq_handler, tool); 848 + 849 + tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "softirq_noise", 850 + timerlat_aa_softirq_handler, tool); 851 + 852 + tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "thread_noise", 853 + timerlat_aa_thread_handler, tool); 854 + 855 + tep_unregister_event_handler(tool->trace.tep, -1, "ftrace", "kernel_stack", 856 + timerlat_aa_stack_handler, tool); 857 + if (!dump_tasks) 858 + return; 859 + 860 + tracefs_event_disable(tool->trace.inst, "sched", "sched_switch"); 861 + tep_unregister_event_handler(tool->trace.tep, -1, "sched", "sched_switch", 862 + timerlat_aa_sched_switch_handler, tool); 863 + 864 + tracefs_event_disable(tool->trace.inst, "workqueue", "workqueue_execute_start"); 865 + tep_unregister_event_handler(tool->trace.tep, -1, "workqueue", "workqueue_execute_start", 866 + timerlat_aa_kworker_start_handler, tool); 867 + } 868 + 869 + /* 870 + * timerlat_aa_register_events - Register events used in the auto-analysis 871 + * 872 + * Returns 0 on success, -1 otherwise. 873 + */ 874 + static int timerlat_aa_register_events(struct osnoise_tool *tool, int dump_tasks) 875 + { 876 + int retval; 877 + 878 + /* 879 + * register auto-analysis handlers. 880 + */ 881 + retval = tracefs_event_enable(tool->trace.inst, "osnoise", NULL); 882 + if (retval < 0 && !errno) { 883 + err_msg("Could not find osnoise events\n"); 884 + goto out_err; 885 + } 886 + 887 + tep_register_event_handler(tool->trace.tep, -1, "osnoise", "nmi_noise", 888 + timerlat_aa_nmi_handler, tool); 889 + 890 + tep_register_event_handler(tool->trace.tep, -1, "osnoise", "irq_noise", 891 + timerlat_aa_irq_handler, tool); 892 + 893 + tep_register_event_handler(tool->trace.tep, -1, "osnoise", "softirq_noise", 894 + timerlat_aa_softirq_handler, tool); 895 + 896 + tep_register_event_handler(tool->trace.tep, -1, "osnoise", "thread_noise", 897 + timerlat_aa_thread_handler, tool); 898 + 899 + tep_register_event_handler(tool->trace.tep, -1, "ftrace", "kernel_stack", 900 + timerlat_aa_stack_handler, tool); 901 + 902 + if (!dump_tasks) 903 + return 0; 904 + 905 + /* 906 + * Dump task events. 907 + */ 908 + retval = tracefs_event_enable(tool->trace.inst, "sched", "sched_switch"); 909 + if (retval < 0 && !errno) { 910 + err_msg("Could not find sched_switch\n"); 911 + goto out_err; 912 + } 913 + 914 + tep_register_event_handler(tool->trace.tep, -1, "sched", "sched_switch", 915 + timerlat_aa_sched_switch_handler, tool); 916 + 917 + retval = tracefs_event_enable(tool->trace.inst, "workqueue", "workqueue_execute_start"); 918 + if (retval < 0 && !errno) { 919 + err_msg("Could not find workqueue_execute_start\n"); 920 + goto out_err; 921 + } 922 + 923 + tep_register_event_handler(tool->trace.tep, -1, "workqueue", "workqueue_execute_start", 924 + timerlat_aa_kworker_start_handler, tool); 925 + 926 + return 0; 927 + 928 + out_err: 929 + timerlat_aa_unregister_events(tool, dump_tasks); 930 + return -1; 931 + } 932 + 933 + /** 934 + * timerlat_aa_destroy - Destroy timerlat auto-analysis 935 + */ 936 + void timerlat_aa_destroy(void) 937 + { 938 + struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); 939 + 940 + if (!taa_ctx) 941 + return; 942 + 943 + if (!taa_ctx->taa_data) 944 + goto out_ctx; 945 + 946 + timerlat_aa_unregister_events(taa_ctx->tool, taa_ctx->dump_tasks); 947 + timerlat_aa_destroy_seqs(taa_ctx); 948 + free(taa_ctx->taa_data); 949 + out_ctx: 950 + free(taa_ctx); 951 + } 952 + 953 + /** 954 + * timerlat_aa_init - Initialize timerlat auto-analysis 955 + * 956 + * Returns 0 on success, -1 otherwise. 957 + */ 958 + int timerlat_aa_init(struct osnoise_tool *tool, int nr_cpus, int dump_tasks) 959 + { 960 + struct timerlat_aa_context *taa_ctx; 961 + int retval; 962 + 963 + taa_ctx = calloc(1, sizeof(*taa_ctx)); 964 + if (!taa_ctx) 965 + return -1; 966 + 967 + __timerlat_aa_ctx = taa_ctx; 968 + 969 + taa_ctx->nr_cpus = nr_cpus; 970 + taa_ctx->tool = tool; 971 + taa_ctx->dump_tasks = dump_tasks; 972 + 973 + taa_ctx->taa_data = calloc(nr_cpus, sizeof(*taa_ctx->taa_data)); 974 + if (!taa_ctx->taa_data) 975 + goto out_err; 976 + 977 + retval = timerlat_aa_init_seqs(taa_ctx); 978 + if (retval) 979 + goto out_err; 980 + 981 + retval = timerlat_aa_register_events(tool, dump_tasks); 982 + if (retval) 983 + goto out_err; 984 + 985 + return 0; 986 + 987 + out_err: 988 + timerlat_aa_destroy(); 989 + return -1; 990 + }
+12
tools/tracing/rtla/src/timerlat_aa.h
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> 4 + */ 5 + 6 + int timerlat_aa_init(struct osnoise_tool *tool, int nr_cpus, int dump_task); 7 + void timerlat_aa_destroy(void); 8 + 9 + int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record, 10 + struct tep_event *event, void *context); 11 + 12 + void timerlat_auto_analysis(int irq_thresh, int thread_thresh);
+44 -2
tools/tracing/rtla/src/timerlat_top.c
··· 10 10 #include <unistd.h> 11 11 #include <stdio.h> 12 12 #include <time.h> 13 + #include <errno.h> 13 14 14 15 #include "utils.h" 15 16 #include "osnoise.h" 16 17 #include "timerlat.h" 18 + #include "timerlat_aa.h" 17 19 18 20 struct timerlat_top_params { 19 21 char *cpus; ··· 32 30 int quiet; 33 31 int set_sched; 34 32 int dma_latency; 33 + int no_aa; 34 + int dump_tasks; 35 35 struct sched_attr sched_param; 36 36 struct trace_events *events; 37 37 }; ··· 134 130 struct tep_event *event, void *context) 135 131 { 136 132 struct trace_instance *trace = context; 133 + struct timerlat_top_params *params; 137 134 unsigned long long latency, thread; 138 135 struct osnoise_tool *top; 139 136 int cpu = record->cpu; 140 137 141 138 top = container_of(trace, struct osnoise_tool, trace); 139 + params = top->params; 142 140 143 141 tep_get_field_val(s, event, "context", record, &thread, 1); 144 142 tep_get_field_val(s, event, "timer_latency", record, &latency, 1); 145 143 146 144 timerlat_top_update(top, cpu, thread, latency); 145 + 146 + if (!params->no_aa) 147 + timerlat_aa_handler(s, record, event, context); 147 148 148 149 return 0; 149 150 } ··· 290 281 " -c/--cpus cpus: run the tracer only on the given cpus", 291 282 " -d/--duration time[m|h|d]: duration of the session in seconds", 292 283 " -D/--debug: print debug info", 284 + " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)", 293 285 " -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]", 294 286 " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", 295 287 " --filter <command>: enable a trace event filter to the previous -e event", 296 288 " --trigger <command>: enable a trace event trigger to the previous -e event", 297 289 " -n/--nano: display data in nanoseconds", 290 + " --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage", 298 291 " -q/--quiet print only a summary at the end", 299 292 " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", 300 293 " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", ··· 360 349 {"trigger", required_argument, 0, '0'}, 361 350 {"filter", required_argument, 0, '1'}, 362 351 {"dma-latency", required_argument, 0, '2'}, 352 + {"no-aa", no_argument, 0, '3'}, 353 + {"dump-tasks", no_argument, 0, '4'}, 363 354 {0, 0, 0, 0} 364 355 }; 365 356 366 357 /* getopt_long stores the option index here. */ 367 358 int option_index = 0; 368 359 369 - c = getopt_long(argc, argv, "a:c:d:De:hi:np:P:qs:t::T:0:1:2:", 360 + c = getopt_long(argc, argv, "a:c:d:De:hi:np:P:qs:t::T:0:1:2:34", 370 361 long_options, &option_index); 371 362 372 363 /* detect the end of the options. */ ··· 381 368 382 369 /* set thread stop to auto_thresh */ 383 370 params->stop_total_us = auto_thresh; 371 + params->stop_us = auto_thresh; 384 372 385 373 /* get stack trace */ 386 374 params->print_stack = auto_thresh; 387 375 388 376 /* set trace */ 389 377 params->trace_output = "timerlat_trace.txt"; 390 - 391 378 break; 392 379 case 'c': 393 380 retval = parse_cpu_list(optarg, &params->monitored_cpus); ··· 450 437 params->trace_output = &optarg[1]; 451 438 else 452 439 params->trace_output = "timerlat_trace.txt"; 440 + 453 441 break; 454 442 case '0': /* trigger */ 455 443 if (params->events) { ··· 481 467 exit(EXIT_FAILURE); 482 468 } 483 469 break; 470 + case '3': /* no-aa */ 471 + params->no_aa = 1; 472 + break; 473 + case '4': 474 + params->dump_tasks = 1; 475 + break; 484 476 default: 485 477 timerlat_top_usage("Invalid option"); 486 478 } ··· 496 476 err_msg("rtla needs root permission\n"); 497 477 exit(EXIT_FAILURE); 498 478 } 479 + 480 + /* 481 + * Auto analysis only happens if stop tracing, thus: 482 + */ 483 + if (!params->stop_us && !params->stop_total_us) 484 + params->no_aa = 1; 499 485 500 486 return params; 501 487 } ··· 573 547 { 574 548 struct osnoise_tool *top; 575 549 int nr_cpus; 550 + int retval; 576 551 577 552 nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 578 553 ··· 589 562 590 563 tep_register_event_handler(top->trace.tep, -1, "ftrace", "timerlat", 591 564 timerlat_top_handler, top); 565 + 566 + /* 567 + * If no auto analysis, we are ready. 568 + */ 569 + if (params->no_aa) 570 + return top; 571 + 572 + retval = timerlat_aa_init(top, nr_cpus, params->dump_tasks); 573 + if (retval) 574 + goto out_err; 592 575 593 576 return top; 594 577 ··· 725 688 726 689 if (trace_is_off(&top->trace, &record->trace)) { 727 690 printf("rtla timerlat hit stop tracing\n"); 691 + 692 + if (!params->no_aa) 693 + timerlat_auto_analysis(params->stop_us, params->stop_total_us); 694 + 728 695 if (params->trace_output) { 729 696 printf(" Saving trace to %s\n", params->trace_output); 730 697 save_trace_to_file(record->trace.inst, params->trace_output); ··· 742 701 params->events = NULL; 743 702 out_free: 744 703 timerlat_free_top(top->data); 704 + timerlat_aa_destroy(); 745 705 osnoise_destroy_tool(record); 746 706 osnoise_destroy_tool(top); 747 707 free(params);
+3
tools/tracing/rtla/src/utils.h
··· 56 56 int parse_prio(char *arg, struct sched_attr *sched_param); 57 57 int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr); 58 58 int set_cpu_dma_latency(int32_t latency); 59 + 60 + #define ns_to_usf(x) (((double)x/1000)) 61 + #define ns_to_per(total, part) ((part * 100) / (double)total)
+1 -1
tools/verification/rv/src/in_kernel.c
··· 519 519 520 520 start = ++end; 521 521 end = strstr(start, "\n"); 522 - }; 522 + } 523 523 524 524 fprintf(stderr, "\n"); 525 525 }