rtla/timerlat_top: Add timerlat user-space support
[linux-2.6-microblaze.git] / tools / tracing / rtla / src / timerlat_top.c
index 02cff49..1640f12 100644 (file)
 #include <time.h>
 #include <errno.h>
 #include <sched.h>
+#include <pthread.h>
 
 #include "utils.h"
 #include "osnoise.h"
 #include "timerlat.h"
 #include "timerlat_aa.h"
+#include "timerlat_u.h"
 
 struct timerlat_top_params {
        char                    *cpus;
@@ -40,6 +42,7 @@ struct timerlat_top_params {
        int                     dump_tasks;
        int                     cgroup;
        int                     hk_cpus;
+       int                     user_top;
        cpu_set_t               hk_cpu_set;
        struct sched_attr       sched_param;
        struct trace_events     *events;
@@ -48,6 +51,7 @@ struct timerlat_top_params {
 struct timerlat_top_cpu {
        int                     irq_count;
        int                     thread_count;
+       int                     user_count;
 
        unsigned long long      cur_irq;
        unsigned long long      min_irq;
@@ -58,6 +62,11 @@ struct timerlat_top_cpu {
        unsigned long long      min_thread;
        unsigned long long      sum_thread;
        unsigned long long      max_thread;
+
+       unsigned long long      cur_user;
+       unsigned long long      min_user;
+       unsigned long long      sum_user;
+       unsigned long long      max_user;
 };
 
 struct timerlat_top_data {
@@ -98,6 +107,7 @@ static struct timerlat_top_data *timerlat_alloc_top(int nr_cpus)
        for (cpu = 0; cpu < nr_cpus; cpu++) {
                data->cpu_data[cpu].min_irq = ~0;
                data->cpu_data[cpu].min_thread = ~0;
+               data->cpu_data[cpu].min_user = ~0;
        }
 
        return data;
@@ -124,12 +134,18 @@ timerlat_top_update(struct osnoise_tool *tool, int cpu,
                update_min(&cpu_data->min_irq, &latency);
                update_sum(&cpu_data->sum_irq, &latency);
                update_max(&cpu_data->max_irq, &latency);
-       } else {
+       } else if (thread == 1) {
                cpu_data->thread_count++;
                cpu_data->cur_thread = latency;
                update_min(&cpu_data->min_thread, &latency);
                update_sum(&cpu_data->sum_thread, &latency);
                update_max(&cpu_data->max_thread, &latency);
+       } else {
+               cpu_data->user_count++;
+               cpu_data->cur_user = latency;
+               update_min(&cpu_data->min_user, &latency);
+               update_sum(&cpu_data->sum_user, &latency);
+               update_max(&cpu_data->max_user, &latency);
        }
 }
 
@@ -172,15 +188,25 @@ static void timerlat_top_header(struct osnoise_tool *top)
 
        trace_seq_printf(s, "\033[2;37;40m");
        trace_seq_printf(s, "                                     Timer Latency                                              ");
+       if (params->user_top)
+               trace_seq_printf(s, "                                         ");
        trace_seq_printf(s, "\033[0;0;0m");
        trace_seq_printf(s, "\n");
 
-       trace_seq_printf(s, "%-6s   |          IRQ Timer Latency (%s)        |         Thread Timer Latency (%s)\n", duration,
+       trace_seq_printf(s, "%-6s   |          IRQ Timer Latency (%s)        |         Thread Timer Latency (%s)", duration,
                        params->output_divisor == 1 ? "ns" : "us",
                        params->output_divisor == 1 ? "ns" : "us");
 
+       if (params->user_top) {
+               trace_seq_printf(s, "      |    Ret user Timer Latency (%s)",
+                               params->output_divisor == 1 ? "ns" : "us");
+       }
+
+       trace_seq_printf(s, "\n");
        trace_seq_printf(s, "\033[2;30;47m");
        trace_seq_printf(s, "CPU COUNT      |      cur       min       avg       max |      cur       min       avg       max");
+       if (params->user_top)
+               trace_seq_printf(s, " |      cur       min       avg       max");
        trace_seq_printf(s, "\033[0;0;0m");
        trace_seq_printf(s, "\n");
 }
@@ -233,7 +259,27 @@ static void timerlat_top_print(struct osnoise_tool *top, int cpu)
                trace_seq_printf(s, "%9llu ", cpu_data->min_thread / divisor);
                trace_seq_printf(s, "%9llu ",
                                (cpu_data->sum_thread / cpu_data->thread_count) / divisor);
-               trace_seq_printf(s, "%9llu\n", cpu_data->max_thread / divisor);
+               trace_seq_printf(s, "%9llu", cpu_data->max_thread / divisor);
+       }
+
+       if (!params->user_top) {
+               trace_seq_printf(s, "\n");
+               return;
+       }
+
+       trace_seq_printf(s, " |");
+
+       if (!cpu_data->user_count) {
+               trace_seq_printf(s, "        - ");
+               trace_seq_printf(s, "        - ");
+               trace_seq_printf(s, "        - ");
+               trace_seq_printf(s, "        -\n");
+       } else {
+               trace_seq_printf(s, "%9llu ", cpu_data->cur_user / divisor);
+               trace_seq_printf(s, "%9llu ", cpu_data->min_user / divisor);
+               trace_seq_printf(s, "%9llu ",
+                               (cpu_data->sum_user / cpu_data->user_count) / divisor);
+               trace_seq_printf(s, "%9llu\n", cpu_data->max_user / divisor);
        }
 }
 
@@ -288,7 +334,7 @@ static void timerlat_top_usage(char *usage)
                "",
                "  usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\",
                "         [[-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",
-               "         [-P priority] [--dma-latency us] [--aa-only us] [-C[=cgroup_name]]",
+               "         [-P priority] [--dma-latency us] [--aa-only us] [-C[=cgroup_name]] [-u]",
                "",
                "         -h/--help: print this menu",
                "         -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
@@ -317,6 +363,7 @@ static void timerlat_top_usage(char *usage)
                "               f:prio - use SCHED_FIFO with prio",
                "               d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
                "                                                      in nanoseconds",
+               "         -u/--user-threads: use rtla user-space threads instead of in-kernel timerlat threads",
                NULL,
        };
 
@@ -371,6 +418,7 @@ static struct timerlat_top_params
                        {"stack",               required_argument,      0, 's'},
                        {"thread",              required_argument,      0, 'T'},
                        {"trace",               optional_argument,      0, 't'},
+                       {"user-threads",        no_argument,            0, 'u'},
                        {"trigger",             required_argument,      0, '0'},
                        {"filter",              required_argument,      0, '1'},
                        {"dma-latency",         required_argument,      0, '2'},
@@ -383,7 +431,7 @@ static struct timerlat_top_params
                /* getopt_long stores the option index here. */
                int option_index = 0;
 
-               c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:np:P:qs:t::T:0:1:2:345:",
+               c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:np:P:qs:t::T:u0:1:2:345:",
                                 long_options, &option_index);
 
                /* detect the end of the options. */
@@ -498,6 +546,9 @@ static struct timerlat_top_params
                        else
                                params->trace_output = "timerlat_trace.txt";
 
+                       break;
+               case 'u':
+                       params->user_top = true;
                        break;
                case '0': /* trigger */
                        if (params->events) {
@@ -563,6 +614,7 @@ static int
 timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_top_params *params)
 {
        int retval;
+       int i;
 
        if (!params->sleep_time)
                params->sleep_time = 1;
@@ -573,6 +625,9 @@ timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_top_params *
                        err_msg("Failed to apply CPUs config\n");
                        goto out_err;
                }
+       } else {
+               for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)
+                       CPU_SET(i, &params->monitored_cpus);
        }
 
        if (params->stop_us) {
@@ -627,6 +682,14 @@ timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_top_params *
                auto_house_keeping(&params->monitored_cpus);
        }
 
+       if (params->user_top) {
+               retval = osnoise_set_workload(top->context, 0);
+               if (retval) {
+                       err_msg("Failed to set OSNOISE_WORKLOAD option\n");
+                       goto out_err;
+               }
+       }
+
        return 0;
 
 out_err:
@@ -687,10 +750,12 @@ int timerlat_top_main(int argc, char *argv[])
 {
        struct timerlat_top_params *params;
        struct osnoise_tool *record = NULL;
+       struct timerlat_u_params params_u;
        struct osnoise_tool *top = NULL;
        struct osnoise_tool *aa = NULL;
        struct trace_instance *trace;
        int dma_latency_fd = -1;
+       pthread_t timerlat_u;
        int return_value = 1;
        char *max_lat;
        int retval;
@@ -727,7 +792,7 @@ int timerlat_top_main(int argc, char *argv[])
                }
        }
 
-       if (params->cgroup) {
+       if (params->cgroup && !params->user_top) {
                retval = set_comm_cgroup("timerlat/", params->cgroup_name);
                if (!retval) {
                        err_msg("Failed to move threads to cgroup\n");
@@ -800,6 +865,25 @@ int timerlat_top_main(int argc, char *argv[])
        top->start_time = time(NULL);
        timerlat_top_set_signals(params);
 
+       if (params->user_top) {
+               /* rtla asked to stop */
+               params_u.should_run = 1;
+               /* all threads left */
+               params_u.stopped_running = 0;
+
+               params_u.set = &params->monitored_cpus;
+               if (params->set_sched)
+                       params_u.sched_param = &params->sched_param;
+               else
+                       params_u.sched_param = NULL;
+
+               params_u.cgroup_name = params->cgroup_name;
+
+               retval = pthread_create(&timerlat_u, NULL, timerlat_u_dispatcher, &params_u);
+               if (retval)
+                       err_msg("Error creating timerlat user-space threads\n");
+       }
+
        while (!stop_tracing) {
                sleep(params->sleep_time);
 
@@ -823,6 +907,18 @@ int timerlat_top_main(int argc, char *argv[])
                if (trace_is_off(&top->trace, &record->trace))
                        break;
 
+               /* is there still any user-threads ? */
+               if (params->user_top) {
+                       if (params_u.stopped_running) {
+                               debug_msg("timerlat user space threads stopped!\n");
+                               break;
+                       }
+               }
+       }
+
+       if (params->user_top && !params_u.stopped_running) {
+               params_u.should_run = 0;
+               sleep(1);
        }
 
        timerlat_print_stats(params, top);