rtla: Updates for v6.18

- This update is mostly just consolidating code between osnoise/timerlat
   and top/hist for easier maintenance and less future divergence.
 -----BEGIN PGP SIGNATURE-----
 
 iIoEABYKADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCaN/guhQccm9zdGVkdEBn
 b29kbWlzLm9yZwAKCRAp5XQQmuv6qqU9AQCO+u+Qmx678DCfDJo9X1UPDtS/bM5f
 r30X1pwYfZ3nNAEA47hbkVFcryFJZbrIPxuTGb0GSM36PHAxmch4QAwBqgs=
 =qzZh
 -----END PGP SIGNATURE-----

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

Pull tracing tools updates from Steven Rostedt

 - This is mostly just consolidating code between osnoise/timerlat and
   top/hist for easier maintenance and less future divergence

* tag 'trace-tools-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  tools/rtla: Add remaining support for osnoise actions
  tools/rtla: Add test engine support for unexpected output
  tools/rtla: Fix -A option name in test comment
  tools/rtla: Consolidate code between osnoise/timerlat and hist/top
  tools/rtla: Create common_apply_config()
  tools/rtla: Move top/hist params into common struct
  tools/rtla: Consolidate common parameters into shared structure
This commit is contained in:
Linus Torvalds 2025-10-05 09:38:26 -07:00
commit d9f24f8e60
26 changed files with 1404 additions and 1720 deletions

View File

@ -53,6 +53,67 @@
**--trace-buffer-size** *kB*
Set the per-cpu trace buffer size in kB for the tracing output.
**--on-threshold** *action*
Defines an action to be executed when tracing is stopped on a latency threshold
specified by |threshold|.
Multiple --on-threshold actions may be specified, and they will be executed in
the order they are provided. If any action fails, subsequent actions in the list
will not be executed.
Supported actions are:
- *trace[,file=<filename>]*
Saves trace output, optionally taking a filename. Alternative to -t/--trace.
Note that nlike -t/--trace, specifying this multiple times will result in
the trace being saved multiple times.
- *signal,num=<sig>,pid=<pid>*
Sends signal to process. "parent" might be specified in place of pid to target
the parent process of rtla.
- *shell,command=<command>*
Execute shell command.
- *continue*
Continue tracing after actions are executed instead of stopping.
Example:
$ rtla |tool| |thresharg| 20 --on-threshold trace
--on-threshold shell,command="grep ipi_send |tracer|\_trace.txt"
--on-threshold signal,num=2,pid=parent
This will save a trace with the default filename "|tracer|\_trace.txt", print its
lines that contain the text "ipi_send" on standard output, and send signal 2
(SIGINT) to the parent process.
Performance Considerations:
|actionsperf|
**--on-end** *action*
Defines an action to be executed at the end of tracing.
Multiple --on-end actions can be specified, and they will be executed in the order
they are provided. If any action fails, subsequent actions in the list will not be
executed.
See the documentation for **--on-threshold** for the list of supported actions, with
the exception that *continue* has no effect.
Example:
$ rtla |tool| -d 5s --on-end trace
This runs rtla with the default options, and saves trace output at the end.
**-h**, **--help**
Print help menu.

View File

@ -1,3 +1,11 @@
.. |threshold| replace:: **-a/--auto**, **-s/--stop**, or **-S/--stop-total**
.. |thresharg| replace:: -s
.. |tracer| replace:: osnoise
.. |actionsperf| replace::
Due to implementational limitations, actions might be delayed
up to one second after tracing is stopped.
**-a**, **--auto** *us*
Set the automatic trace mode. This mode sets some commonly used options

View File

@ -1,3 +1,13 @@
.. |threshold| replace:: **-a/--auto**, **-i/--irq**, or **-T/--thread**
.. |thresharg| replace:: -T
.. |tracer| replace:: timerlat
.. |actionsperf| replace::
For time-sensitive actions, it is recommended to run **rtla timerlat** with BPF
support and RT priority. Note that due to implementational limitations, actions
might be delayed up to one second after tracing is stopped if BPF mode is not
available or disabled.
**-a**, **--auto** *us*
Set the automatic trace mode. This mode sets some commonly used options
@ -55,67 +65,3 @@
Set timerlat to run without workload, waiting for the user to dispatch a per-cpu
task that waits for a new period on the tracing/osnoise/per_cpu/cpu$ID/timerlat_fd.
See linux/tools/rtla/sample/timerlat_load.py for an example of user-load code.
**--on-threshold** *action*
Defines an action to be executed when tracing is stopped on a latency threshold
specified by **-i/--irq** or **-T/--thread**.
Multiple --on-threshold actions may be specified, and they will be executed in
the order they are provided. If any action fails, subsequent actions in the list
will not be executed.
Supported actions are:
- *trace[,file=<filename>]*
Saves trace output, optionally taking a filename. Alternative to -t/--trace.
Note that nlike -t/--trace, specifying this multiple times will result in
the trace being saved multiple times.
- *signal,num=<sig>,pid=<pid>*
Sends signal to process. "parent" might be specified in place of pid to target
the parent process of rtla.
- *shell,command=<command>*
Execute shell command.
- *continue*
Continue tracing after actions are executed instead of stopping.
Example:
$ rtla timerlat -T 20 --on-threshold trace
--on-threshold shell,command="grep ipi_send timerlat_trace.txt"
--on-threshold signal,num=2,pid=parent
This will save a trace with the default filename "timerlat_trace.txt", print its
lines that contain the text "ipi_send" on standard output, and send signal 2
(SIGINT) to the parent process.
Performance Considerations:
For time-sensitive actions, it is recommended to run **rtla timerlat** with BPF
support and RT priority. Note that due to implementational limitations, actions
might be delayed up to one second after tracing is stopped if BPF mode is not
available or disabled.
**--on-end** *action*
Defines an action to be executed at the end of **rtla timerlat** tracing.
Multiple --on-end actions can be specified, and they will be executed in the order
they are provided. If any action fails, subsequent actions in the list will not be
executed.
See the documentation for **--on-threshold** for the list of supported actions, with
the exception that *continue* has no effect.
Example:
$ rtla timerlat -d 5s --on-end trace
This runs rtla timerlat with default options and save trace output at the end.

View File

@ -1,5 +1,7 @@
.. SPDX-License-Identifier: GPL-2.0
.. |tool| replace:: hwnoise
============
rtla-hwnoise
============

View File

@ -1,3 +1,5 @@
.. |tool| replace:: osnoise hist
===================
rtla-osnoise-hist
===================

View File

@ -1,3 +1,5 @@
.. |tool| replace:: osnoise top
===================
rtla-osnoise-top
===================

View File

@ -1,3 +1,5 @@
.. |tool| replace:: timerlat hist
=====================
rtla-timerlat-hist
=====================

View File

@ -1,3 +1,5 @@
.. |tool| replace:: timerlat top
====================
rtla-timerlat-top
====================

View File

@ -1,6 +1,7 @@
rtla-y += trace.o
rtla-y += utils.o
rtla-y += actions.o
rtla-y += common.o
rtla-y += osnoise.o
rtla-y += osnoise_top.o
rtla-y += osnoise_hist.o

View File

@ -127,17 +127,17 @@ actions_add_continue(struct actions *self)
* actions_parse - add an action based on text specification
*/
int
actions_parse(struct actions *self, const char *trigger)
actions_parse(struct actions *self, const char *trigger, const char *tracefn)
{
enum action_type type = ACTION_NONE;
char *token;
const char *token;
char trigger_c[strlen(trigger) + 1];
/* For ACTION_SIGNAL */
int signal = 0, pid = 0;
/* For ACTION_TRACE_OUTPUT */
char *trace_output;
const char *trace_output;
strcpy(trigger_c, trigger);
token = strtok(trigger_c, ",");
@ -160,7 +160,7 @@ actions_parse(struct actions *self, const char *trigger)
case ACTION_TRACE_OUTPUT:
/* Takes no argument */
if (token == NULL)
trace_output = "timerlat_trace.txt";
trace_output = tracefn;
else {
if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) {
trace_output = token + 5;

View File

@ -48,5 +48,5 @@ int actions_add_trace_output(struct actions *self, const char *trace_output);
int actions_add_signal(struct actions *self, int signal, int pid);
int actions_add_shell(struct actions *self, const char *command);
int actions_add_continue(struct actions *self);
int actions_parse(struct actions *self, const char *trigger);
int actions_parse(struct actions *self, const char *trigger, const char *tracefn);
int actions_perform(struct actions *self);

View File

@ -0,0 +1,344 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include "common.h"
struct trace_instance *trace_inst;
int stop_tracing;
static void stop_trace(int sig)
{
if (stop_tracing) {
/*
* Stop requested twice in a row; abort event processing and
* exit immediately
*/
tracefs_iterate_stop(trace_inst->inst);
return;
}
stop_tracing = 1;
if (trace_inst)
trace_instance_stop(trace_inst);
}
/*
* set_signals - handles the signal to stop the tool
*/
static void set_signals(struct common_params *params)
{
signal(SIGINT, stop_trace);
if (params->duration) {
signal(SIGALRM, stop_trace);
alarm(params->duration);
}
}
/*
* common_apply_config - apply common configs to the initialized tool
*/
int
common_apply_config(struct osnoise_tool *tool, struct common_params *params)
{
int retval, i;
if (!params->sleep_time)
params->sleep_time = 1;
retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all");
if (retval) {
err_msg("Failed to apply CPUs config\n");
goto out_err;
}
if (!params->cpus) {
for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)
CPU_SET(i, &params->monitored_cpus);
}
if (params->hk_cpus) {
retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
&params->hk_cpu_set);
if (retval == -1) {
err_msg("Failed to set rtla to the house keeping CPUs\n");
goto out_err;
}
} else if (params->cpus) {
/*
* Even if the user do not set a house-keeping CPU, try to
* move rtla to a CPU set different to the one where the user
* set the workload to run.
*
* No need to check results as this is an automatic attempt.
*/
auto_house_keeping(&params->monitored_cpus);
}
/*
* Set workload according to type of thread if the kernel supports it.
* On kernels without support, user threads will have already failed
* on missing fd, and kernel threads do not need it.
*/
retval = osnoise_set_workload(tool->context, params->kernel_workload);
if (retval < -1) {
err_msg("Failed to set OSNOISE_WORKLOAD option\n");
goto out_err;
}
return 0;
out_err:
return -1;
}
int run_tool(struct tool_ops *ops, int argc, char *argv[])
{
struct common_params *params;
enum result return_value = ERROR;
struct osnoise_tool *tool;
bool stopped;
int retval;
params = ops->parse_args(argc, argv);
if (!params)
exit(1);
tool = ops->init_tool(params);
if (!tool) {
err_msg("Could not init osnoise tool\n");
goto out_exit;
}
tool->ops = ops;
tool->params = params;
/*
* Save trace instance into global variable so that SIGINT can stop
* the timerlat tracer.
* Otherwise, rtla could loop indefinitely when overloaded.
*/
trace_inst = &tool->trace;
retval = ops->apply_config(tool);
if (retval) {
err_msg("Could not apply config\n");
goto out_free;
}
retval = enable_tracer_by_name(trace_inst->inst, ops->tracer);
if (retval) {
err_msg("Failed to enable %s tracer\n", ops->tracer);
goto out_free;
}
if (params->set_sched) {
retval = set_comm_sched_attr(ops->comm_prefix, &params->sched_param);
if (retval) {
err_msg("Failed to set sched parameters\n");
goto out_free;
}
}
if (params->cgroup && !params->user_data) {
retval = set_comm_cgroup(ops->comm_prefix, params->cgroup_name);
if (!retval) {
err_msg("Failed to move threads to cgroup\n");
goto out_free;
}
}
if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->end_actions.present[ACTION_TRACE_OUTPUT]) {
tool->record = osnoise_init_trace_tool(ops->tracer);
if (!tool->record) {
err_msg("Failed to enable the trace instance\n");
goto out_free;
}
params->threshold_actions.trace_output_inst = tool->record->trace.inst;
params->end_actions.trace_output_inst = tool->record->trace.inst;
if (params->events) {
retval = trace_events_enable(&tool->record->trace, params->events);
if (retval)
goto out_trace;
}
if (params->buffer_size > 0) {
retval = trace_set_buffer_size(&tool->record->trace, params->buffer_size);
if (retval)
goto out_trace;
}
}
if (params->user_workload) {
pthread_t user_thread;
/* rtla asked to stop */
params->user.should_run = 1;
/* all threads left */
params->user.stopped_running = 0;
params->user.set = &params->monitored_cpus;
if (params->set_sched)
params->user.sched_param = &params->sched_param;
else
params->user.sched_param = NULL;
params->user.cgroup_name = params->cgroup_name;
retval = pthread_create(&user_thread, NULL, timerlat_u_dispatcher, &params->user);
if (retval)
err_msg("Error creating timerlat user-space threads\n");
}
retval = ops->enable(tool);
if (retval)
goto out_trace;
tool->start_time = time(NULL);
set_signals(params);
retval = ops->main(tool);
if (retval)
goto out_trace;
if (params->user_workload && !params->user.stopped_running) {
params->user.should_run = 0;
sleep(1);
}
ops->print_stats(tool);
actions_perform(&params->end_actions);
return_value = PASSED;
stopped = osnoise_trace_is_off(tool, tool->record) && !stop_tracing;
if (stopped) {
printf("%s hit stop tracing\n", ops->tracer);
return_value = FAILED;
}
if (ops->analyze)
ops->analyze(tool, stopped);
out_trace:
trace_events_destroy(&tool->record->trace, params->events);
params->events = NULL;
out_free:
ops->free(tool);
osnoise_destroy_tool(tool->record);
osnoise_destroy_tool(tool);
actions_destroy(&params->threshold_actions);
actions_destroy(&params->end_actions);
free(params);
out_exit:
exit(return_value);
}
int top_main_loop(struct osnoise_tool *tool)
{
struct common_params *params = tool->params;
struct trace_instance *trace = &tool->trace;
struct osnoise_tool *record = tool->record;
int retval;
while (!stop_tracing) {
sleep(params->sleep_time);
if (params->aa_only && !osnoise_trace_is_off(tool, record))
continue;
retval = tracefs_iterate_raw_events(trace->tep,
trace->inst,
NULL,
0,
collect_registered_events,
trace);
if (retval < 0) {
err_msg("Error iterating on events\n");
return retval;
}
if (!params->quiet)
tool->ops->print_stats(tool);
if (osnoise_trace_is_off(tool, record)) {
actions_perform(&params->threshold_actions);
if (!params->threshold_actions.continue_flag)
/* continue flag not set, break */
return 0;
/* continue action reached, re-enable tracing */
if (record)
trace_instance_start(&record->trace);
if (tool->aa)
trace_instance_start(&tool->aa->trace);
trace_instance_start(trace);
}
/* is there still any user-threads ? */
if (params->user_workload) {
if (params->user.stopped_running) {
debug_msg("timerlat user space threads stopped!\n");
break;
}
}
}
return 0;
}
int hist_main_loop(struct osnoise_tool *tool)
{
struct common_params *params = tool->params;
struct trace_instance *trace = &tool->trace;
int retval = 0;
while (!stop_tracing) {
sleep(params->sleep_time);
retval = tracefs_iterate_raw_events(trace->tep,
trace->inst,
NULL,
0,
collect_registered_events,
trace);
if (retval < 0) {
err_msg("Error iterating on events\n");
break;
}
if (osnoise_trace_is_off(tool, tool->record)) {
actions_perform(&params->threshold_actions);
if (!params->threshold_actions.continue_flag) {
/* continue flag not set, break */
break;
/* continue action reached, re-enable tracing */
if (tool->record)
trace_instance_start(&tool->record->trace);
if (tool->aa)
trace_instance_start(&tool->aa->trace);
trace_instance_start(&tool->trace);
}
break;
}
/* is there still any user-threads ? */
if (params->user_workload) {
if (params->user.stopped_running) {
debug_msg("user-space threads stopped!\n");
break;
}
}
}
return retval;
}

View File

@ -0,0 +1,154 @@
/* SPDX-License-Identifier: GPL-2.0 */
#pragma once
#include "actions.h"
#include "timerlat_u.h"
#include "trace.h"
#include "utils.h"
/*
* osnoise_context - read, store, write, restore osnoise configs.
*/
struct osnoise_context {
int flags;
int ref;
char *curr_cpus;
char *orig_cpus;
/* 0 as init value */
unsigned long long orig_runtime_us;
unsigned long long runtime_us;
/* 0 as init value */
unsigned long long orig_period_us;
unsigned long long period_us;
/* 0 as init value */
long long orig_timerlat_period_us;
long long timerlat_period_us;
/* 0 as init value */
long long orig_tracing_thresh;
long long tracing_thresh;
/* -1 as init value because 0 is disabled */
long long orig_stop_us;
long long stop_us;
/* -1 as init value because 0 is disabled */
long long orig_stop_total_us;
long long stop_total_us;
/* -1 as init value because 0 is disabled */
long long orig_print_stack;
long long print_stack;
/* -1 as init value because 0 is off */
int orig_opt_irq_disable;
int opt_irq_disable;
/* -1 as init value because 0 is off */
int orig_opt_workload;
int opt_workload;
};
extern struct trace_instance *trace_inst;
extern int stop_tracing;
struct hist_params {
char no_irq;
char no_thread;
char no_header;
char no_summary;
char no_index;
char with_zeros;
int bucket_size;
int entries;
};
/*
* common_params - Parameters shared between timerlat_params and osnoise_params
*/
struct common_params {
/* trace configuration */
char *cpus;
cpu_set_t monitored_cpus;
struct trace_events *events;
int buffer_size;
/* Timing parameters */
int warmup;
long long stop_us;
long long stop_total_us;
int sleep_time;
int duration;
/* Scheduling parameters */
int set_sched;
struct sched_attr sched_param;
int cgroup;
char *cgroup_name;
int hk_cpus;
cpu_set_t hk_cpu_set;
/* Other parameters */
struct hist_params hist;
int output_divisor;
int pretty_output;
int quiet;
int user_workload;
int kernel_workload;
int user_data;
int aa_only;
struct actions threshold_actions;
struct actions end_actions;
struct timerlat_u_params user;
};
struct tool_ops;
/*
* osnoise_tool - osnoise based tool definition.
*
* Only the "trace" and "context" fields are used for
* the additional trace instances (record and aa).
*/
struct osnoise_tool {
struct tool_ops *ops;
struct trace_instance trace;
struct osnoise_context *context;
void *data;
struct common_params *params;
time_t start_time;
struct osnoise_tool *record;
struct osnoise_tool *aa;
};
struct tool_ops {
const char *tracer;
const char *comm_prefix;
struct common_params *(*parse_args)(int argc, char *argv[]);
struct osnoise_tool *(*init_tool)(struct common_params *params);
int (*apply_config)(struct osnoise_tool *tool);
int (*enable)(struct osnoise_tool *tool);
int (*main)(struct osnoise_tool *tool);
void (*print_stats)(struct osnoise_tool *tool);
void (*analyze)(struct osnoise_tool *tool, bool stopped);
void (*free)(struct osnoise_tool *tool);
};
int osnoise_set_cpus(struct osnoise_context *context, char *cpus);
void osnoise_restore_cpus(struct osnoise_context *context);
int osnoise_set_workload(struct osnoise_context *context, bool onoff);
void osnoise_destroy_tool(struct osnoise_tool *top);
struct osnoise_tool *osnoise_init_tool(char *tool_name);
struct osnoise_tool *osnoise_init_trace_tool(const char *tracer);
bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record);
int common_apply_config(struct osnoise_tool *tool, struct common_params *params);
int top_main_loop(struct osnoise_tool *tool);
int hist_main_loop(struct osnoise_tool *tool);

View File

@ -906,22 +906,6 @@ static void osnoise_put_workload(struct osnoise_context *context)
context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL;
}
/*
* enable_osnoise - enable osnoise tracer in the trace_instance
*/
int enable_osnoise(struct trace_instance *trace)
{
return enable_tracer_by_name(trace->inst, "osnoise");
}
/*
* enable_timerlat - enable timerlat tracer in the trace_instance
*/
int enable_timerlat(struct trace_instance *trace)
{
return enable_tracer_by_name(trace->inst, "timerlat");
}
enum {
FLAG_CONTEXT_NEWLY_CREATED = (1 << 0),
FLAG_CONTEXT_DELETED = (1 << 1),
@ -1056,7 +1040,7 @@ out_err:
/*
* osnoise_init_trace_tool - init a tracer instance to trace osnoise events
*/
struct osnoise_tool *osnoise_init_trace_tool(char *tracer)
struct osnoise_tool *osnoise_init_trace_tool(const char *tracer)
{
struct osnoise_tool *trace;
int retval;
@ -1120,21 +1104,14 @@ osnoise_report_missed_events(struct osnoise_tool *tool)
}
/*
* osnoise_apply_config - apply common configs to the initialized tool
* osnoise_apply_config - apply osnoise configs to the initialized tool
*/
int
osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)
{
int retval;
if (!params->sleep_time)
params->sleep_time = 1;
retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all");
if (retval) {
err_msg("Failed to apply CPUs config\n");
goto out_err;
}
params->common.kernel_workload = true;
if (params->runtime || params->period) {
retval = osnoise_set_runtime_period(tool->context,
@ -1151,13 +1128,13 @@ osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)
goto out_err;
}
retval = osnoise_set_stop_us(tool->context, params->stop_us);
retval = osnoise_set_stop_us(tool->context, params->common.stop_us);
if (retval) {
err_msg("Failed to set stop us\n");
goto out_err;
}
retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us);
if (retval) {
err_msg("Failed to set stop total us\n");
goto out_err;
@ -1169,36 +1146,50 @@ osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)
goto out_err;
}
if (params->hk_cpus) {
retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
&params->hk_cpu_set);
if (retval == -1) {
err_msg("Failed to set rtla to the house keeping CPUs\n");
goto out_err;
}
} else if (params->cpus) {
/*
* Even if the user do not set a house-keeping CPU, try to
* move rtla to a CPU set different to the one where the user
* set the workload to run.
*
* No need to check results as this is an automatic attempt.
*/
auto_house_keeping(&params->monitored_cpus);
}
retval = osnoise_set_workload(tool->context, true);
if (retval < -1) {
err_msg("Failed to set OSNOISE_WORKLOAD option\n");
goto out_err;
}
return 0;
return common_apply_config(tool, &params->common);
out_err:
return -1;
}
int osnoise_enable(struct osnoise_tool *tool)
{
struct osnoise_params *params = to_osnoise_params(tool->params);
int retval;
/*
* Start the tracer here, after having set all instances.
*
* Let the trace instance start first for the case of hitting a stop
* tracing while enabling other instances. The trace instance is the
* one with most valuable information.
*/
if (tool->record)
trace_instance_start(&tool->record->trace);
trace_instance_start(&tool->trace);
if (params->common.warmup > 0) {
debug_msg("Warming up for %d seconds\n", params->common.warmup);
sleep(params->common.warmup);
if (stop_tracing)
return -1;
/*
* Clean up the buffer. The osnoise workload do not run
* with tracing off to avoid creating a performance penalty
* when not needed.
*/
retval = tracefs_instance_file_write(tool->trace.inst, "trace", "");
if (retval < 0) {
debug_msg("Error cleaning up the buffer");
return retval;
}
}
return 0;
}
static void osnoise_usage(int err)
{
int i;
@ -1232,7 +1223,7 @@ int osnoise_main(int argc, char *argv[])
* default cmdline.
*/
if (argc == 1) {
osnoise_top_main(argc, argv);
run_tool(&osnoise_top_ops, argc, argv);
exit(0);
}
@ -1240,13 +1231,13 @@ int osnoise_main(int argc, char *argv[])
osnoise_usage(0);
} else if (strncmp(argv[1], "-", 1) == 0) {
/* the user skipped the tool, call the default one */
osnoise_top_main(argc, argv);
run_tool(&osnoise_top_ops, argc, argv);
exit(0);
} else if (strcmp(argv[1], "top") == 0) {
osnoise_top_main(argc-1, &argv[1]);
run_tool(&osnoise_top_ops, argc-1, &argv[1]);
exit(0);
} else if (strcmp(argv[1], "hist") == 0) {
osnoise_hist_main(argc-1, &argv[1]);
run_tool(&osnoise_hist_ops, argc-1, &argv[1]);
exit(0);
}
@ -1257,6 +1248,6 @@ usage:
int hwnoise_main(int argc, char *argv[])
{
osnoise_top_main(argc, argv);
run_tool(&osnoise_top_ops, argc, argv);
exit(0);
}

View File

@ -1,8 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#pragma once
#include "utils.h"
#include "trace.h"
#include "common.h"
enum osnoise_mode {
MODE_OSNOISE = 0,
@ -10,92 +9,14 @@ enum osnoise_mode {
};
struct osnoise_params {
/* Common params */
char *cpus;
cpu_set_t monitored_cpus;
char *trace_output;
char *cgroup_name;
struct common_params common;
unsigned long long runtime;
unsigned long long period;
long long threshold;
long long stop_us;
long long stop_total_us;
int sleep_time;
int duration;
int set_sched;
int cgroup;
int hk_cpus;
cpu_set_t hk_cpu_set;
struct sched_attr sched_param;
struct trace_events *events;
int warmup;
int buffer_size;
union {
struct {
/* top only */
int quiet;
int pretty_output;
enum osnoise_mode mode;
};
struct {
/* hist only */
int output_divisor;
char no_header;
char no_summary;
char no_index;
char with_zeros;
int bucket_size;
int entries;
};
};
enum osnoise_mode mode;
};
/*
* osnoise_context - read, store, write, restore osnoise configs.
*/
struct osnoise_context {
int flags;
int ref;
char *curr_cpus;
char *orig_cpus;
/* 0 as init value */
unsigned long long orig_runtime_us;
unsigned long long runtime_us;
/* 0 as init value */
unsigned long long orig_period_us;
unsigned long long period_us;
/* 0 as init value */
long long orig_timerlat_period_us;
long long timerlat_period_us;
/* 0 as init value */
long long orig_tracing_thresh;
long long tracing_thresh;
/* -1 as init value because 0 is disabled */
long long orig_stop_us;
long long stop_us;
/* -1 as init value because 0 is disabled */
long long orig_stop_total_us;
long long stop_total_us;
/* -1 as init value because 0 is disabled */
long long orig_print_stack;
long long print_stack;
/* -1 as init value because 0 is off */
int orig_opt_irq_disable;
int opt_irq_disable;
/* -1 as init value because 0 is off */
int orig_opt_workload;
int opt_workload;
};
#define to_osnoise_params(ptr) container_of(ptr, struct osnoise_params, common)
/*
* *_INIT_VALs are also invalid values, they are used to
@ -108,9 +29,6 @@ struct osnoise_context *osnoise_context_alloc(void);
int osnoise_get_context(struct osnoise_context *context);
void osnoise_put_context(struct osnoise_context *context);
int osnoise_set_cpus(struct osnoise_context *context, char *cpus);
void osnoise_restore_cpus(struct osnoise_context *context);
int osnoise_set_runtime_period(struct osnoise_context *context,
unsigned long long runtime,
unsigned long long period);
@ -137,27 +55,17 @@ int osnoise_set_print_stack(struct osnoise_context *context,
long long print_stack);
int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff);
int osnoise_set_workload(struct osnoise_context *context, bool onoff);
/*
* osnoise_tool - osnoise based tool definition.
*/
struct osnoise_tool {
struct trace_instance trace;
struct osnoise_context *context;
void *data;
void *params;
time_t start_time;
};
void osnoise_destroy_tool(struct osnoise_tool *top);
struct osnoise_tool *osnoise_init_tool(char *tool_name);
struct osnoise_tool *osnoise_init_trace_tool(char *tracer);
void osnoise_report_missed_events(struct osnoise_tool *tool);
bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record);
int osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params);
int osnoise_hist_main(int argc, char *argv[]);
int osnoise_top_main(int argc, char **argv);
int osnoise_enable(struct osnoise_tool *tool);
int osnoise_main(int argc, char **argv);
int hwnoise_main(int argc, char **argv);
extern struct tool_ops timerlat_top_ops, timerlat_hist_ops;
extern struct tool_ops osnoise_top_ops, osnoise_hist_ops;
int run_tool(struct tool_ops *ops, int argc, char *argv[]);
int hist_main_loop(struct osnoise_tool *tool);

View File

@ -54,6 +54,11 @@ osnoise_free_histogram(struct osnoise_hist_data *data)
free(data);
}
static void osnoise_free_hist_tool(struct osnoise_tool *tool)
{
osnoise_free_histogram(tool->data);
}
/*
* osnoise_alloc_histogram - alloc runtime data
*/
@ -95,15 +100,15 @@ cleanup:
static void osnoise_hist_update_multiple(struct osnoise_tool *tool, int cpu,
unsigned long long duration, int count)
{
struct osnoise_params *params = tool->params;
struct osnoise_params *params = to_osnoise_params(tool->params);
struct osnoise_hist_data *data = tool->data;
unsigned long long total_duration;
int entries = data->entries;
int bucket;
int *hist;
if (params->output_divisor)
duration = duration / params->output_divisor;
if (params->common.output_divisor)
duration = duration / params->common.output_divisor;
bucket = duration / data->bucket_size;
@ -137,7 +142,7 @@ static void osnoise_destroy_trace_hist(struct osnoise_tool *tool)
*/
static int osnoise_init_trace_hist(struct osnoise_tool *tool)
{
struct osnoise_params *params = tool->params;
struct osnoise_params *params = to_osnoise_params(tool->params);
struct osnoise_hist_data *data = tool->data;
int bucket_size;
char buff[128];
@ -146,7 +151,7 @@ static int osnoise_init_trace_hist(struct osnoise_tool *tool)
/*
* Set the size of the bucket.
*/
bucket_size = params->output_divisor * params->bucket_size;
bucket_size = params->common.output_divisor * params->common.hist.bucket_size;
snprintf(buff, sizeof(buff), "duration.buckets=%d", bucket_size);
data->trace_hist = tracefs_hist_alloc(tool->trace.tep, "osnoise", "sample_threshold",
@ -222,28 +227,28 @@ static void osnoise_read_trace_hist(struct osnoise_tool *tool)
*/
static void osnoise_hist_header(struct osnoise_tool *tool)
{
struct osnoise_params *params = tool->params;
struct osnoise_params *params = to_osnoise_params(tool->params);
struct osnoise_hist_data *data = tool->data;
struct trace_seq *s = tool->trace.seq;
char duration[26];
int cpu;
if (params->no_header)
if (params->common.hist.no_header)
return;
get_duration(tool->start_time, duration, sizeof(duration));
trace_seq_printf(s, "# RTLA osnoise histogram\n");
trace_seq_printf(s, "# Time unit is %s (%s)\n",
params->output_divisor == 1 ? "nanoseconds" : "microseconds",
params->output_divisor == 1 ? "ns" : "us");
params->common.output_divisor == 1 ? "nanoseconds" : "microseconds",
params->common.output_divisor == 1 ? "ns" : "us");
trace_seq_printf(s, "# Duration: %s\n", duration);
if (!params->no_index)
if (!params->common.hist.no_index)
trace_seq_printf(s, "Index");
for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(cpu, &params->common.monitored_cpus))
continue;
if (!data->hist[cpu].count)
@ -267,14 +272,14 @@ osnoise_print_summary(struct osnoise_params *params,
{
int cpu;
if (params->no_summary)
if (params->common.hist.no_summary)
return;
if (!params->no_index)
if (!params->common.hist.no_index)
trace_seq_printf(trace->seq, "count:");
for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(cpu, &params->common.monitored_cpus))
continue;
if (!data->hist[cpu].count)
@ -284,11 +289,11 @@ osnoise_print_summary(struct osnoise_params *params,
}
trace_seq_printf(trace->seq, "\n");
if (!params->no_index)
if (!params->common.hist.no_index)
trace_seq_printf(trace->seq, "min: ");
for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(cpu, &params->common.monitored_cpus))
continue;
if (!data->hist[cpu].count)
@ -299,11 +304,11 @@ osnoise_print_summary(struct osnoise_params *params,
}
trace_seq_printf(trace->seq, "\n");
if (!params->no_index)
if (!params->common.hist.no_index)
trace_seq_printf(trace->seq, "avg: ");
for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(cpu, &params->common.monitored_cpus))
continue;
if (!data->hist[cpu].count)
@ -317,11 +322,11 @@ osnoise_print_summary(struct osnoise_params *params,
}
trace_seq_printf(trace->seq, "\n");
if (!params->no_index)
if (!params->common.hist.no_index)
trace_seq_printf(trace->seq, "max: ");
for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(cpu, &params->common.monitored_cpus))
continue;
if (!data->hist[cpu].count)
@ -339,8 +344,9 @@ osnoise_print_summary(struct osnoise_params *params,
* osnoise_print_stats - print data for all CPUs
*/
static void
osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool)
osnoise_print_stats(struct osnoise_tool *tool)
{
struct osnoise_params *params = to_osnoise_params(tool->params);
struct osnoise_hist_data *data = tool->data;
struct trace_instance *trace = &tool->trace;
int has_samples = 0;
@ -352,12 +358,12 @@ osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool)
for (bucket = 0; bucket < data->entries; bucket++) {
total = 0;
if (!params->no_index)
if (!params->common.hist.no_index)
trace_seq_printf(trace->seq, "%-6d",
bucket * data->bucket_size);
for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(cpu, &params->common.monitored_cpus))
continue;
if (!data->hist[cpu].count)
@ -367,7 +373,7 @@ osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool)
trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].samples[bucket]);
}
if (total == 0 && !params->with_zeros) {
if (total == 0 && !params->common.hist.with_zeros) {
trace_seq_reset(trace->seq);
continue;
}
@ -391,11 +397,11 @@ osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool)
return;
}
if (!params->no_index)
if (!params->common.hist.no_index)
trace_seq_printf(trace->seq, "over: ");
for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(cpu, &params->common.monitored_cpus))
continue;
if (!data->hist[cpu].count)
@ -456,6 +462,8 @@ static void osnoise_hist_usage(char *usage)
" in nanoseconds",
" --warm-up: let the workload run for s seconds before collecting data",
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
" --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed",
" --on-end <action>: define action to be executed at measurement end, multiple are allowed",
NULL,
};
@ -477,22 +485,26 @@ static void osnoise_hist_usage(char *usage)
/*
* osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters
*/
static struct osnoise_params
static struct common_params
*osnoise_hist_parse_args(int argc, char *argv[])
{
struct osnoise_params *params;
struct trace_events *tevent;
int retval;
int c;
char *trace_output = NULL;
params = calloc(1, sizeof(*params));
if (!params)
exit(1);
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
/* display data in microseconds */
params->output_divisor = 1000;
params->bucket_size = 1;
params->entries = 256;
params->common.output_divisor = 1000;
params->common.hist.bucket_size = 1;
params->common.hist.entries = 256;
while (1) {
static struct option long_options[] = {
@ -521,6 +533,8 @@ static struct osnoise_params
{"filter", required_argument, 0, '5'},
{"warm-up", required_argument, 0, '6'},
{"trace-buffer-size", required_argument, 0, '7'},
{"on-threshold", required_argument, 0, '8'},
{"on-end", required_argument, 0, '9'},
{0, 0, 0, 0}
};
@ -537,42 +551,43 @@ static struct osnoise_params
switch (c) {
case 'a':
/* set sample stop to auto_thresh */
params->stop_us = get_llong_from_str(optarg);
params->common.stop_us = get_llong_from_str(optarg);
/* set sample threshold to 1 */
params->threshold = 1;
/* set trace */
params->trace_output = "osnoise_trace.txt";
trace_output = "osnoise_trace.txt";
break;
case 'b':
params->bucket_size = get_llong_from_str(optarg);
if ((params->bucket_size == 0) || (params->bucket_size >= 1000000))
params->common.hist.bucket_size = get_llong_from_str(optarg);
if (params->common.hist.bucket_size == 0 ||
params->common.hist.bucket_size >= 1000000)
osnoise_hist_usage("Bucket size needs to be > 0 and <= 1000000\n");
break;
case 'c':
retval = parse_cpu_set(optarg, &params->monitored_cpus);
retval = parse_cpu_set(optarg, &params->common.monitored_cpus);
if (retval)
osnoise_hist_usage("\nInvalid -c cpu list\n");
params->cpus = optarg;
params->common.cpus = optarg;
break;
case 'C':
params->cgroup = 1;
params->common.cgroup = 1;
if (!optarg) {
/* will inherit this cgroup */
params->cgroup_name = NULL;
params->common.cgroup_name = NULL;
} else if (*optarg == '=') {
/* skip the = */
params->cgroup_name = ++optarg;
params->common.cgroup_name = ++optarg;
}
break;
case 'D':
config_debug = 1;
break;
case 'd':
params->duration = parse_seconds_duration(optarg);
if (!params->duration)
params->common.duration = parse_seconds_duration(optarg);
if (!params->common.duration)
osnoise_hist_usage("Invalid -D duration\n");
break;
case 'e':
@ -582,14 +597,15 @@ static struct osnoise_params
exit(EXIT_FAILURE);
}
if (params->events)
tevent->next = params->events;
if (params->common.events)
tevent->next = params->common.events;
params->events = tevent;
params->common.events = tevent;
break;
case 'E':
params->entries = get_llong_from_str(optarg);
if ((params->entries < 10) || (params->entries > 9999999))
params->common.hist.entries = get_llong_from_str(optarg);
if (params->common.hist.entries < 10 ||
params->common.hist.entries > 9999999)
osnoise_hist_usage("Entries must be > 10 and < 9999999\n");
break;
case 'h':
@ -597,8 +613,8 @@ static struct osnoise_params
osnoise_hist_usage(NULL);
break;
case 'H':
params->hk_cpus = 1;
retval = parse_cpu_set(optarg, &params->hk_cpu_set);
params->common.hk_cpus = 1;
retval = parse_cpu_set(optarg, &params->common.hk_cpu_set);
if (retval) {
err_msg("Error parsing house keeping CPUs\n");
exit(EXIT_FAILURE);
@ -610,10 +626,10 @@ static struct osnoise_params
osnoise_hist_usage("Period longer than 10 s\n");
break;
case 'P':
retval = parse_prio(optarg, &params->sched_param);
retval = parse_prio(optarg, &params->common.sched_param);
if (retval == -1)
osnoise_hist_usage("Invalid -P priority");
params->set_sched = 1;
params->common.set_sched = 1;
break;
case 'r':
params->runtime = get_llong_from_str(optarg);
@ -621,10 +637,10 @@ static struct osnoise_params
osnoise_hist_usage("Runtime shorter than 100 us\n");
break;
case 's':
params->stop_us = get_llong_from_str(optarg);
params->common.stop_us = get_llong_from_str(optarg);
break;
case 'S':
params->stop_total_us = get_llong_from_str(optarg);
params->common.stop_total_us = get_llong_from_str(optarg);
break;
case 'T':
params->threshold = get_llong_from_str(optarg);
@ -632,29 +648,29 @@ static struct osnoise_params
case 't':
if (optarg) {
if (optarg[0] == '=')
params->trace_output = &optarg[1];
trace_output = &optarg[1];
else
params->trace_output = &optarg[0];
trace_output = &optarg[0];
} else if (optind < argc && argv[optind][0] != '0')
params->trace_output = argv[optind];
trace_output = argv[optind];
else
params->trace_output = "osnoise_trace.txt";
trace_output = "osnoise_trace.txt";
break;
case '0': /* no header */
params->no_header = 1;
params->common.hist.no_header = 1;
break;
case '1': /* no summary */
params->no_summary = 1;
params->common.hist.no_summary = 1;
break;
case '2': /* no index */
params->no_index = 1;
params->common.hist.no_index = 1;
break;
case '3': /* with zeros */
params->with_zeros = 1;
params->common.hist.with_zeros = 1;
break;
case '4': /* trigger */
if (params->events) {
retval = trace_event_add_trigger(params->events, optarg);
if (params->common.events) {
retval = trace_event_add_trigger(params->common.events, optarg);
if (retval) {
err_msg("Error adding trigger %s\n", optarg);
exit(EXIT_FAILURE);
@ -664,8 +680,8 @@ static struct osnoise_params
}
break;
case '5': /* filter */
if (params->events) {
retval = trace_event_add_filter(params->events, optarg);
if (params->common.events) {
retval = trace_event_add_filter(params->common.events, optarg);
if (retval) {
err_msg("Error adding filter %s\n", optarg);
exit(EXIT_FAILURE);
@ -675,50 +691,60 @@ static struct osnoise_params
}
break;
case '6':
params->warmup = get_llong_from_str(optarg);
params->common.warmup = get_llong_from_str(optarg);
break;
case '7':
params->buffer_size = get_llong_from_str(optarg);
params->common.buffer_size = get_llong_from_str(optarg);
break;
case '8':
retval = actions_parse(&params->common.threshold_actions, optarg,
"osnoise_trace.txt");
if (retval) {
err_msg("Invalid action %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case '9':
retval = actions_parse(&params->common.end_actions, optarg,
"osnoise_trace.txt");
if (retval) {
err_msg("Invalid action %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
default:
osnoise_hist_usage("Invalid option");
}
}
if (trace_output)
actions_add_trace_output(&params->common.threshold_actions, trace_output);
if (geteuid()) {
err_msg("rtla needs root permission\n");
exit(EXIT_FAILURE);
}
if (params->no_index && !params->with_zeros)
if (params->common.hist.no_index && !params->common.hist.with_zeros)
osnoise_hist_usage("no-index set and with-zeros not set - it does not make sense");
return params;
return &params->common;
}
/*
* osnoise_hist_apply_config - apply the hist configs to the initialized tool
*/
static int
osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)
osnoise_hist_apply_config(struct osnoise_tool *tool)
{
int retval;
retval = osnoise_apply_config(tool, params);
if (retval)
goto out_err;
return 0;
out_err:
return -1;
return osnoise_apply_config(tool, to_osnoise_params(tool->params));
}
/*
* osnoise_init_hist - initialize a osnoise hist tool with parameters
*/
static struct osnoise_tool
*osnoise_init_hist(struct osnoise_params *params)
*osnoise_init_hist(struct common_params *params)
{
struct osnoise_tool *tool;
int nr_cpus;
@ -729,12 +755,11 @@ static struct osnoise_tool
if (!tool)
return NULL;
tool->data = osnoise_alloc_histogram(nr_cpus, params->entries, params->bucket_size);
tool->data = osnoise_alloc_histogram(nr_cpus, params->hist.entries,
params->hist.bucket_size);
if (!tool->data)
goto out_err;
tool->params = params;
return tool;
out_err:
@ -742,171 +767,35 @@ out_err:
return NULL;
}
static int stop_tracing;
static void stop_hist(int sig)
static int osnoise_hist_enable(struct osnoise_tool *tool)
{
stop_tracing = 1;
}
/*
* osnoise_hist_set_signals - handles the signal to stop the tool
*/
static void
osnoise_hist_set_signals(struct osnoise_params *params)
{
signal(SIGINT, stop_hist);
if (params->duration) {
signal(SIGALRM, stop_hist);
alarm(params->duration);
}
}
int osnoise_hist_main(int argc, char *argv[])
{
struct osnoise_params *params;
struct osnoise_tool *record = NULL;
struct osnoise_tool *tool = NULL;
enum result return_value = ERROR;
struct trace_instance *trace;
int retval;
params = osnoise_hist_parse_args(argc, argv);
if (!params)
exit(1);
tool = osnoise_init_hist(params);
if (!tool) {
err_msg("Could not init osnoise hist\n");
goto out_exit;
}
retval = osnoise_hist_apply_config(tool, params);
if (retval) {
err_msg("Could not apply config\n");
goto out_destroy;
}
trace = &tool->trace;
retval = enable_osnoise(trace);
if (retval) {
err_msg("Failed to enable osnoise tracer\n");
goto out_destroy;
}
retval = osnoise_init_trace_hist(tool);
if (retval)
goto out_destroy;
return retval;
if (params->set_sched) {
retval = set_comm_sched_attr("osnoise/", &params->sched_param);
if (retval) {
err_msg("Failed to set sched parameters\n");
goto out_free;
}
}
return osnoise_enable(tool);
}
if (params->cgroup) {
retval = set_comm_cgroup("timerlat/", params->cgroup_name);
if (!retval) {
err_msg("Failed to move threads to cgroup\n");
goto out_free;
}
}
if (params->trace_output) {
record = osnoise_init_trace_tool("osnoise");
if (!record) {
err_msg("Failed to enable the trace instance\n");
goto out_free;
}
if (params->events) {
retval = trace_events_enable(&record->trace, params->events);
if (retval)
goto out_hist;
}
if (params->buffer_size > 0) {
retval = trace_set_buffer_size(&record->trace, params->buffer_size);
if (retval)
goto out_hist;
}
}
/*
* Start the tracer here, after having set all instances.
*
* Let the trace instance start first for the case of hitting a stop
* tracing while enabling other instances. The trace instance is the
* one with most valuable information.
*/
if (params->trace_output)
trace_instance_start(&record->trace);
trace_instance_start(trace);
if (params->warmup > 0) {
debug_msg("Warming up for %d seconds\n", params->warmup);
sleep(params->warmup);
if (stop_tracing)
goto out_hist;
/*
* Clean up the buffer. The osnoise workload do not run
* with tracing off to avoid creating a performance penalty
* when not needed.
*/
retval = tracefs_instance_file_write(trace->inst, "trace", "");
if (retval < 0) {
debug_msg("Error cleaning up the buffer");
goto out_hist;
}
}
tool->start_time = time(NULL);
osnoise_hist_set_signals(params);
while (!stop_tracing) {
sleep(params->sleep_time);
retval = tracefs_iterate_raw_events(trace->tep,
trace->inst,
NULL,
0,
collect_registered_events,
trace);
if (retval < 0) {
err_msg("Error iterating on events\n");
goto out_hist;
}
if (osnoise_trace_is_off(tool, record))
break;
}
static int osnoise_hist_main_loop(struct osnoise_tool *tool)
{
int retval;
retval = hist_main_loop(tool);
osnoise_read_trace_hist(tool);
osnoise_print_stats(params, tool);
return_value = PASSED;
if (osnoise_trace_is_off(tool, record)) {
printf("rtla osnoise hit stop tracing\n");
save_trace_to_file(record ? record->trace.inst : NULL,
params->trace_output);
return_value = FAILED;
}
out_hist:
trace_events_destroy(&record->trace, params->events);
params->events = NULL;
out_free:
osnoise_free_histogram(tool->data);
out_destroy:
osnoise_destroy_tool(record);
osnoise_destroy_tool(tool);
free(params);
out_exit:
exit(return_value);
return retval;
}
struct tool_ops osnoise_hist_ops = {
.tracer = "osnoise",
.comm_prefix = "osnoise/",
.parse_args = osnoise_hist_parse_args,
.init_tool = osnoise_init_hist,
.apply_config = osnoise_hist_apply_config,
.enable = osnoise_hist_enable,
.main = osnoise_hist_main_loop,
.print_stats = osnoise_print_stats,
.free = osnoise_free_hist_tool,
};

View File

@ -37,13 +37,17 @@ struct osnoise_top_data {
/*
* osnoise_free_top - free runtime data
*/
static void
osnoise_free_top(struct osnoise_top_data *data)
static void osnoise_free_top(struct osnoise_top_data *data)
{
free(data->cpu_data);
free(data);
}
static void osnoise_free_top_tool(struct osnoise_tool *tool)
{
osnoise_free_top(tool->data);
}
/*
* osnoise_alloc_histogram - alloc runtime data
*/
@ -123,13 +127,14 @@ osnoise_top_handler(struct trace_seq *s, struct tep_record *record,
*/
static void osnoise_top_header(struct osnoise_tool *top)
{
struct osnoise_params *params = top->params;
struct osnoise_params *params = to_osnoise_params(top->params);
struct trace_seq *s = top->trace.seq;
bool pretty = params->common.pretty_output;
char duration[26];
get_duration(top->start_time, duration, sizeof(duration));
if (params->pretty_output)
if (pretty)
trace_seq_printf(s, "\033[2;37;40m");
trace_seq_printf(s, " ");
@ -143,13 +148,13 @@ static void osnoise_top_header(struct osnoise_tool *top)
trace_seq_printf(s, " ");
if (params->pretty_output)
if (pretty)
trace_seq_printf(s, "\033[0;0;0m");
trace_seq_printf(s, "\n");
trace_seq_printf(s, "duration: %9s | time is in us\n", duration);
if (params->pretty_output)
if (pretty)
trace_seq_printf(s, "\033[2;30;47m");
trace_seq_printf(s, "CPU Period Runtime ");
@ -164,7 +169,7 @@ static void osnoise_top_header(struct osnoise_tool *top)
trace_seq_printf(s, " IRQ Softirq Thread");
eol:
if (params->pretty_output)
if (pretty)
trace_seq_printf(s, "\033[0;0;0m");
trace_seq_printf(s, "\n");
}
@ -183,7 +188,7 @@ static void clear_terminal(struct trace_seq *seq)
*/
static void osnoise_top_print(struct osnoise_tool *tool, int cpu)
{
struct osnoise_params *params = tool->params;
struct osnoise_params *params = to_osnoise_params(tool->params);
struct trace_seq *s = tool->trace.seq;
struct osnoise_top_cpu *cpu_data;
struct osnoise_top_data *data;
@ -223,8 +228,9 @@ static void osnoise_top_print(struct osnoise_tool *tool, int cpu)
* osnoise_print_stats - print data for all cpus
*/
static void
osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *top)
osnoise_print_stats(struct osnoise_tool *top)
{
struct osnoise_params *params = to_osnoise_params(top->params);
struct trace_instance *trace = &top->trace;
static int nr_cpus = -1;
int i;
@ -232,13 +238,13 @@ osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *top)
if (nr_cpus == -1)
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
if (!params->quiet)
if (!params->common.quiet)
clear_terminal(trace->seq);
osnoise_top_header(top);
for (i = 0; i < nr_cpus; i++) {
if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(i, &params->common.monitored_cpus))
continue;
osnoise_top_print(top, i);
}
@ -285,6 +291,8 @@ static void osnoise_top_usage(struct osnoise_params *params, char *usage)
" in nanoseconds",
" --warm-up s: let the workload run for s seconds before collecting data",
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
" --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed",
" --on-end: define action to be executed at measurement end, multiple are allowed",
NULL,
};
@ -319,17 +327,21 @@ static void osnoise_top_usage(struct osnoise_params *params, char *usage)
/*
* osnoise_top_parse_args - allocs, parse and fill the cmd line parameters
*/
struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
struct common_params *osnoise_top_parse_args(int argc, char **argv)
{
struct osnoise_params *params;
struct trace_events *tevent;
int retval;
int c;
char *trace_output = NULL;
params = calloc(1, sizeof(*params));
if (!params)
exit(1);
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
if (strcmp(argv[0], "hwnoise") == 0) {
params->mode = MODE_HWNOISE;
/*
@ -361,6 +373,8 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
{"filter", required_argument, 0, '1'},
{"warm-up", required_argument, 0, '2'},
{"trace-buffer-size", required_argument, 0, '3'},
{"on-threshold", required_argument, 0, '4'},
{"on-end", required_argument, 0, '5'},
{0, 0, 0, 0}
};
@ -377,37 +391,37 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
switch (c) {
case 'a':
/* set sample stop to auto_thresh */
params->stop_us = get_llong_from_str(optarg);
params->common.stop_us = get_llong_from_str(optarg);
/* set sample threshold to 1 */
params->threshold = 1;
/* set trace */
params->trace_output = "osnoise_trace.txt";
trace_output = "osnoise_trace.txt";
break;
case 'c':
retval = parse_cpu_set(optarg, &params->monitored_cpus);
retval = parse_cpu_set(optarg, &params->common.monitored_cpus);
if (retval)
osnoise_top_usage(params, "\nInvalid -c cpu list\n");
params->cpus = optarg;
params->common.cpus = optarg;
break;
case 'C':
params->cgroup = 1;
params->common.cgroup = 1;
if (!optarg) {
/* will inherit this cgroup */
params->cgroup_name = NULL;
params->common.cgroup_name = NULL;
} else if (*optarg == '=') {
/* skip the = */
params->cgroup_name = ++optarg;
params->common.cgroup_name = ++optarg;
}
break;
case 'D':
config_debug = 1;
break;
case 'd':
params->duration = parse_seconds_duration(optarg);
if (!params->duration)
params->common.duration = parse_seconds_duration(optarg);
if (!params->common.duration)
osnoise_top_usage(params, "Invalid -d duration\n");
break;
case 'e':
@ -417,9 +431,9 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
exit(EXIT_FAILURE);
}
if (params->events)
tevent->next = params->events;
params->events = tevent;
if (params->common.events)
tevent->next = params->common.events;
params->common.events = tevent;
break;
case 'h':
@ -427,8 +441,8 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
osnoise_top_usage(params, NULL);
break;
case 'H':
params->hk_cpus = 1;
retval = parse_cpu_set(optarg, &params->hk_cpu_set);
params->common.hk_cpus = 1;
retval = parse_cpu_set(optarg, &params->common.hk_cpu_set);
if (retval) {
err_msg("Error parsing house keeping CPUs\n");
exit(EXIT_FAILURE);
@ -440,13 +454,13 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
osnoise_top_usage(params, "Period longer than 10 s\n");
break;
case 'P':
retval = parse_prio(optarg, &params->sched_param);
retval = parse_prio(optarg, &params->common.sched_param);
if (retval == -1)
osnoise_top_usage(params, "Invalid -P priority");
params->set_sched = 1;
params->common.set_sched = 1;
break;
case 'q':
params->quiet = 1;
params->common.quiet = 1;
break;
case 'r':
params->runtime = get_llong_from_str(optarg);
@ -454,28 +468,28 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
osnoise_top_usage(params, "Runtime shorter than 100 us\n");
break;
case 's':
params->stop_us = get_llong_from_str(optarg);
params->common.stop_us = get_llong_from_str(optarg);
break;
case 'S':
params->stop_total_us = get_llong_from_str(optarg);
params->common.stop_total_us = get_llong_from_str(optarg);
break;
case 't':
if (optarg) {
if (optarg[0] == '=')
params->trace_output = &optarg[1];
trace_output = &optarg[1];
else
params->trace_output = &optarg[0];
trace_output = &optarg[0];
} else if (optind < argc && argv[optind][0] != '-')
params->trace_output = argv[optind];
trace_output = argv[optind];
else
params->trace_output = "osnoise_trace.txt";
trace_output = "osnoise_trace.txt";
break;
case 'T':
params->threshold = get_llong_from_str(optarg);
break;
case '0': /* trigger */
if (params->events) {
retval = trace_event_add_trigger(params->events, optarg);
if (params->common.events) {
retval = trace_event_add_trigger(params->common.events, optarg);
if (retval) {
err_msg("Error adding trigger %s\n", optarg);
exit(EXIT_FAILURE);
@ -485,8 +499,8 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
}
break;
case '1': /* filter */
if (params->events) {
retval = trace_event_add_filter(params->events, optarg);
if (params->common.events) {
retval = trace_event_add_filter(params->common.events, optarg);
if (retval) {
err_msg("Error adding filter %s\n", optarg);
exit(EXIT_FAILURE);
@ -496,30 +510,50 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
}
break;
case '2':
params->warmup = get_llong_from_str(optarg);
params->common.warmup = get_llong_from_str(optarg);
break;
case '3':
params->buffer_size = get_llong_from_str(optarg);
params->common.buffer_size = get_llong_from_str(optarg);
break;
case '4':
retval = actions_parse(&params->common.threshold_actions, optarg,
"osnoise_trace.txt");
if (retval) {
err_msg("Invalid action %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case '5':
retval = actions_parse(&params->common.end_actions, optarg,
"osnoise_trace.txt");
if (retval) {
err_msg("Invalid action %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
default:
osnoise_top_usage(params, "Invalid option");
}
}
if (trace_output)
actions_add_trace_output(&params->common.threshold_actions, trace_output);
if (geteuid()) {
err_msg("osnoise needs root permission\n");
exit(EXIT_FAILURE);
}
return params;
return &params->common;
}
/*
* osnoise_top_apply_config - apply the top configs to the initialized tool
*/
static int
osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)
osnoise_top_apply_config(struct osnoise_tool *tool)
{
struct osnoise_params *params = to_osnoise_params(tool->params);
int retval;
retval = osnoise_apply_config(tool, params);
@ -534,8 +568,8 @@ osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_params *param
}
}
if (isatty(STDOUT_FILENO) && !params->quiet)
params->pretty_output = 1;
if (isatty(STDOUT_FILENO) && !params->common.quiet)
params->common.pretty_output = 1;
return 0;
@ -546,7 +580,7 @@ out_err:
/*
* osnoise_init_top - initialize a osnoise top tool with parameters
*/
struct osnoise_tool *osnoise_init_top(struct osnoise_params *params)
struct osnoise_tool *osnoise_init_top(struct common_params *params)
{
struct osnoise_tool *tool;
int nr_cpus;
@ -563,175 +597,20 @@ struct osnoise_tool *osnoise_init_top(struct osnoise_params *params)
return NULL;
}
tool->params = params;
tep_register_event_handler(tool->trace.tep, -1, "ftrace", "osnoise",
osnoise_top_handler, NULL);
return tool;
}
static int stop_tracing;
static void stop_top(int sig)
{
stop_tracing = 1;
}
/*
* osnoise_top_set_signals - handles the signal to stop the tool
*/
static void osnoise_top_set_signals(struct osnoise_params *params)
{
signal(SIGINT, stop_top);
if (params->duration) {
signal(SIGALRM, stop_top);
alarm(params->duration);
}
}
int osnoise_top_main(int argc, char **argv)
{
struct osnoise_params *params;
struct osnoise_tool *record = NULL;
struct osnoise_tool *tool = NULL;
enum result return_value = ERROR;
struct trace_instance *trace;
int retval;
params = osnoise_top_parse_args(argc, argv);
if (!params)
exit(1);
tool = osnoise_init_top(params);
if (!tool) {
err_msg("Could not init osnoise top\n");
goto out_exit;
}
retval = osnoise_top_apply_config(tool, params);
if (retval) {
err_msg("Could not apply config\n");
goto out_free;
}
trace = &tool->trace;
retval = enable_osnoise(trace);
if (retval) {
err_msg("Failed to enable osnoise tracer\n");
goto out_free;
}
if (params->set_sched) {
retval = set_comm_sched_attr("osnoise/", &params->sched_param);
if (retval) {
err_msg("Failed to set sched parameters\n");
goto out_free;
}
}
if (params->cgroup) {
retval = set_comm_cgroup("osnoise/", params->cgroup_name);
if (!retval) {
err_msg("Failed to move threads to cgroup\n");
goto out_free;
}
}
if (params->trace_output) {
record = osnoise_init_trace_tool("osnoise");
if (!record) {
err_msg("Failed to enable the trace instance\n");
goto out_free;
}
if (params->events) {
retval = trace_events_enable(&record->trace, params->events);
if (retval)
goto out_top;
}
if (params->buffer_size > 0) {
retval = trace_set_buffer_size(&record->trace, params->buffer_size);
if (retval)
goto out_top;
}
}
/*
* Start the tracer here, after having set all instances.
*
* Let the trace instance start first for the case of hitting a stop
* tracing while enabling other instances. The trace instance is the
* one with most valuable information.
*/
if (params->trace_output)
trace_instance_start(&record->trace);
trace_instance_start(trace);
if (params->warmup > 0) {
debug_msg("Warming up for %d seconds\n", params->warmup);
sleep(params->warmup);
if (stop_tracing)
goto out_top;
/*
* Clean up the buffer. The osnoise workload do not run
* with tracing off to avoid creating a performance penalty
* when not needed.
*/
retval = tracefs_instance_file_write(trace->inst, "trace", "");
if (retval < 0) {
debug_msg("Error cleaning up the buffer");
goto out_top;
}
}
tool->start_time = time(NULL);
osnoise_top_set_signals(params);
while (!stop_tracing) {
sleep(params->sleep_time);
retval = tracefs_iterate_raw_events(trace->tep,
trace->inst,
NULL,
0,
collect_registered_events,
trace);
if (retval < 0) {
err_msg("Error iterating on events\n");
goto out_top;
}
if (!params->quiet)
osnoise_print_stats(params, tool);
if (osnoise_trace_is_off(tool, record))
break;
}
osnoise_print_stats(params, tool);
return_value = PASSED;
if (osnoise_trace_is_off(tool, record)) {
printf("osnoise hit stop tracing\n");
save_trace_to_file(record ? record->trace.inst : NULL,
params->trace_output);
return_value = FAILED;
}
out_top:
trace_events_destroy(&record->trace, params->events);
params->events = NULL;
out_free:
osnoise_free_top(tool->data);
osnoise_destroy_tool(record);
osnoise_destroy_tool(tool);
free(params);
out_exit:
exit(return_value);
}
struct tool_ops osnoise_top_ops = {
.tracer = "osnoise",
.comm_prefix = "osnoise/",
.parse_args = osnoise_top_parse_args,
.init_tool = osnoise_init_top,
.apply_config = osnoise_top_apply_config,
.enable = osnoise_enable,
.main = top_main_loop,
.print_stats = osnoise_print_stats,
.free = osnoise_free_top_tool,
};

View File

@ -15,29 +15,37 @@
#include <sched.h>
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_bpf.h"
#define DEFAULT_TIMERLAT_PERIOD 1000 /* 1ms */
static int dma_latency_fd = -1;
/*
* timerlat_apply_config - apply common configs to the initialized tool
*/
int
timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params)
{
int retval, i;
int retval;
if (!params->sleep_time)
params->sleep_time = 1;
retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all");
if (retval) {
err_msg("Failed to apply CPUs config\n");
goto out_err;
}
if (!params->cpus) {
for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)
CPU_SET(i, &params->monitored_cpus);
/*
* Try to enable BPF, unless disabled explicitly.
* If BPF enablement fails, fall back to tracefs mode.
*/
if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) {
debug_msg("RTLA_NO_BPF set, disabling BPF\n");
params->mode = TRACING_MODE_TRACEFS;
} else if (!tep_find_event_by_name(tool->trace.tep, "osnoise", "timerlat_sample")) {
debug_msg("osnoise:timerlat_sample missing, disabling BPF\n");
params->mode = TRACING_MODE_TRACEFS;
} else {
retval = timerlat_bpf_init(params);
if (retval) {
debug_msg("Could not enable BPF\n");
params->mode = TRACING_MODE_TRACEFS;
}
}
if (params->mode != TRACING_MODE_BPF) {
@ -45,13 +53,13 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params)
* In tracefs and mixed mode, timerlat tracer handles stopping
* on threshold
*/
retval = osnoise_set_stop_us(tool->context, params->stop_us);
retval = osnoise_set_stop_us(tool->context, params->common.stop_us);
if (retval) {
err_msg("Failed to set stop us\n");
goto out_err;
}
retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us);
if (retval) {
err_msg("Failed to set stop total us\n");
goto out_err;
@ -75,57 +83,159 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params)
goto out_err;
}
if (params->hk_cpus) {
retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
&params->hk_cpu_set);
if (retval == -1) {
err_msg("Failed to set rtla to the house keeping CPUs\n");
goto out_err;
}
} else if (params->cpus) {
/*
* Even if the user do not set a house-keeping CPU, try to
* move rtla to a CPU set different to the one where the user
* set the workload to run.
*
* No need to check results as this is an automatic attempt.
*/
auto_house_keeping(&params->monitored_cpus);
}
/*
* If the user did not specify a type of thread, try user-threads first.
* Fall back to kernel threads otherwise.
*/
if (!params->kernel_workload && !params->user_data) {
if (!params->common.kernel_workload && !params->common.user_data) {
retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd");
if (retval) {
debug_msg("User-space interface detected, setting user-threads\n");
params->user_workload = 1;
params->user_data = 1;
params->common.user_workload = 1;
params->common.user_data = 1;
} else {
debug_msg("User-space interface not detected, setting kernel-threads\n");
params->kernel_workload = 1;
params->common.kernel_workload = 1;
}
}
/*
* Set workload according to type of thread if the kernel supports it.
* On kernels without support, user threads will have already failed
* on missing timerlat_fd, and kernel threads do not need it.
*/
retval = osnoise_set_workload(tool->context, params->kernel_workload);
if (retval < -1) {
err_msg("Failed to set OSNOISE_WORKLOAD option\n");
goto out_err;
}
return 0;
return common_apply_config(tool, &params->common);
out_err:
return -1;
}
int timerlat_enable(struct osnoise_tool *tool)
{
struct timerlat_params *params = to_timerlat_params(tool->params);
int retval, nr_cpus, i;
if (params->dma_latency >= 0) {
dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
if (dma_latency_fd < 0) {
err_msg("Could not set /dev/cpu_dma_latency.\n");
return -1;
}
}
if (params->deepest_idle_state >= -1) {
if (!have_libcpupower_support()) {
err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n");
return -1;
}
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
for (i = 0; i < nr_cpus; i++) {
if (params->common.cpus && !CPU_ISSET(i, &params->common.monitored_cpus))
continue;
if (save_cpu_idle_disable_state(i) < 0) {
err_msg("Could not save cpu idle state.\n");
return -1;
}
if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) {
err_msg("Could not set deepest cpu idle state.\n");
return -1;
}
}
}
if (!params->no_aa) {
tool->aa = osnoise_init_tool("timerlat_aa");
if (!tool->aa)
return -1;
retval = timerlat_aa_init(tool->aa, params->dump_tasks);
if (retval) {
err_msg("Failed to enable the auto analysis instance\n");
return retval;
}
retval = enable_tracer_by_name(tool->aa->trace.inst, "timerlat");
if (retval) {
err_msg("Failed to enable aa tracer\n");
return retval;
}
}
if (params->common.warmup > 0) {
debug_msg("Warming up for %d seconds\n", params->common.warmup);
sleep(params->common.warmup);
if (stop_tracing)
return -1;
}
/*
* Start the tracers here, after having set all instances.
*
* Let the trace instance start first for the case of hitting a stop
* tracing while enabling other instances. The trace instance is the
* one with most valuable information.
*/
if (tool->record)
trace_instance_start(&tool->record->trace);
if (!params->no_aa)
trace_instance_start(&tool->aa->trace);
if (params->mode == TRACING_MODE_TRACEFS) {
trace_instance_start(&tool->trace);
} else {
retval = timerlat_bpf_attach();
if (retval) {
err_msg("Error attaching BPF program\n");
return retval;
}
}
return 0;
}
void timerlat_analyze(struct osnoise_tool *tool, bool stopped)
{
struct timerlat_params *params = to_timerlat_params(tool->params);
if (stopped) {
if (!params->no_aa)
timerlat_auto_analysis(params->common.stop_us,
params->common.stop_total_us);
} else if (params->common.aa_only) {
char *max_lat;
/*
* If the trace did not stop with --aa-only, at least print
* the max known latency.
*/
max_lat = tracefs_instance_file_read(trace_inst->inst, "tracing_max_latency", NULL);
if (max_lat) {
printf(" Max latency was %s\n", max_lat);
free(max_lat);
}
}
}
void timerlat_free(struct osnoise_tool *tool)
{
struct timerlat_params *params = to_timerlat_params(tool->params);
int nr_cpus, i;
timerlat_aa_destroy();
if (dma_latency_fd >= 0)
close(dma_latency_fd);
if (params->deepest_idle_state >= -1) {
for (i = 0; i < nr_cpus; i++) {
if (params->common.cpus &&
!CPU_ISSET(i, &params->common.monitored_cpus))
continue;
restore_cpu_idle_disable_state(i);
}
}
osnoise_destroy_tool(tool->aa);
if (params->mode != TRACING_MODE_TRACEFS)
timerlat_bpf_destroy();
free_cpu_idle_disable_states();
}
static void timerlat_usage(int err)
{
int i;
@ -159,7 +269,7 @@ int timerlat_main(int argc, char *argv[])
* default cmdline.
*/
if (argc == 1) {
timerlat_top_main(argc, argv);
run_tool(&timerlat_top_ops, argc, argv);
exit(0);
}
@ -167,13 +277,13 @@ int timerlat_main(int argc, char *argv[])
timerlat_usage(0);
} else if (strncmp(argv[1], "-", 1) == 0) {
/* the user skipped the tool, call the default one */
timerlat_top_main(argc, argv);
run_tool(&timerlat_top_ops, argc, argv);
exit(0);
} else if (strcmp(argv[1], "top") == 0) {
timerlat_top_main(argc-1, &argv[1]);
run_tool(&timerlat_top_ops, argc-1, &argv[1]);
exit(0);
} else if (strcmp(argv[1], "hist") == 0) {
timerlat_hist_main(argc-1, &argv[1]);
run_tool(&timerlat_hist_ops, argc-1, &argv[1]);
exit(0);
}

View File

@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
#include "actions.h"
#include "osnoise.h"
/*
@ -20,61 +19,21 @@ enum timerlat_tracing_mode {
};
struct timerlat_params {
/* Common params */
char *cpus;
cpu_set_t monitored_cpus;
char *cgroup_name;
unsigned long long runtime;
long long stop_us;
long long stop_total_us;
struct common_params common;
long long timerlat_period_us;
long long print_stack;
int sleep_time;
int output_divisor;
int duration;
int set_sched;
int dma_latency;
int no_aa;
int dump_tasks;
int cgroup;
int hk_cpus;
int user_workload;
int kernel_workload;
int user_data;
int warmup;
int buffer_size;
int deepest_idle_state;
cpu_set_t hk_cpu_set;
struct sched_attr sched_param;
struct trace_events *events;
enum timerlat_tracing_mode mode;
struct actions threshold_actions;
struct actions end_actions;
union {
struct {
/* top only */
int quiet;
int aa_only;
int pretty_output;
};
struct {
/* hist only */
char no_irq;
char no_thread;
char no_header;
char no_summary;
char no_index;
char with_zeros;
int bucket_size;
int entries;
};
};
};
int timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params);
#define to_timerlat_params(ptr) container_of(ptr, struct timerlat_params, common)
int timerlat_hist_main(int argc, char *argv[]);
int timerlat_top_main(int argc, char *argv[]);
int timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params);
int timerlat_main(int argc, char *argv[]);
int timerlat_enable(struct osnoise_tool *tool);
void timerlat_analyze(struct osnoise_tool *tool, bool stopped);
void timerlat_free(struct osnoise_tool *tool);

View File

@ -21,20 +21,20 @@ int timerlat_bpf_init(struct timerlat_params *params)
return 1;
/* Pass common options */
bpf->rodata->output_divisor = params->output_divisor;
bpf->rodata->entries = params->entries;
bpf->rodata->irq_threshold = params->stop_us;
bpf->rodata->thread_threshold = params->stop_total_us;
bpf->rodata->aa_only = params->aa_only;
bpf->rodata->output_divisor = params->common.output_divisor;
bpf->rodata->entries = params->common.hist.entries;
bpf->rodata->irq_threshold = params->common.stop_us;
bpf->rodata->thread_threshold = params->common.stop_total_us;
bpf->rodata->aa_only = params->common.aa_only;
if (params->entries != 0) {
if (params->common.hist.entries != 0) {
/* Pass histogram options */
bpf->rodata->bucket_size = params->bucket_size;
bpf->rodata->bucket_size = params->common.hist.bucket_size;
/* Set histogram array sizes */
bpf_map__set_max_entries(bpf->maps.hist_irq, params->entries);
bpf_map__set_max_entries(bpf->maps.hist_thread, params->entries);
bpf_map__set_max_entries(bpf->maps.hist_user, params->entries);
bpf_map__set_max_entries(bpf->maps.hist_irq, params->common.hist.entries);
bpf_map__set_max_entries(bpf->maps.hist_thread, params->common.hist.entries);
bpf_map__set_max_entries(bpf->maps.hist_user, params->common.hist.entries);
} else {
/* No entries, disable histogram */
bpf_map__set_autocreate(bpf->maps.hist_irq, false);
@ -42,7 +42,7 @@ int timerlat_bpf_init(struct timerlat_params *params)
bpf_map__set_autocreate(bpf->maps.hist_user, false);
}
if (params->aa_only) {
if (params->common.aa_only) {
/* Auto-analysis only, disable summary */
bpf_map__set_autocreate(bpf->maps.summary_irq, false);
bpf_map__set_autocreate(bpf->maps.summary_thread, false);

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,6 @@
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_u.h"
#include "timerlat_bpf.h"
struct timerlat_top_cpu {
@ -49,13 +48,18 @@ struct timerlat_top_data {
/*
* timerlat_free_top - free runtime data
*/
static void
timerlat_free_top(struct timerlat_top_data *data)
static void timerlat_free_top(struct timerlat_top_data *data)
{
free(data->cpu_data);
free(data);
}
static void timerlat_free_top_tool(struct osnoise_tool *tool)
{
timerlat_free_top(tool->data);
timerlat_free(tool);
}
/*
* timerlat_alloc_histogram - alloc runtime data
*/
@ -128,12 +132,12 @@ timerlat_top_update(struct osnoise_tool *tool, int cpu,
unsigned long long thread,
unsigned long long latency)
{
struct timerlat_params *params = tool->params;
struct timerlat_params *params = to_timerlat_params(tool->params);
struct timerlat_top_data *data = tool->data;
struct timerlat_top_cpu *cpu_data = &data->cpu_data[cpu];
if (params->output_divisor)
latency = latency / params->output_divisor;
if (params->common.output_divisor)
latency = latency / params->common.output_divisor;
if (!thread) {
cpu_data->irq_count++;
@ -164,15 +168,13 @@ timerlat_top_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
struct trace_instance *trace = context;
struct timerlat_params *params;
unsigned long long latency, thread;
struct osnoise_tool *top;
int cpu = record->cpu;
top = container_of(trace, struct osnoise_tool, trace);
params = top->params;
if (!params->aa_only) {
if (!top->params->aa_only) {
tep_get_field_val(s, event, "context", record, &thread, 1);
tep_get_field_val(s, event, "timer_latency", record, &latency, 1);
@ -258,39 +260,40 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool)
static void timerlat_top_header(struct timerlat_params *params, struct osnoise_tool *top)
{
struct trace_seq *s = top->trace.seq;
bool pretty = params->common.pretty_output;
char duration[26];
get_duration(top->start_time, duration, sizeof(duration));
if (params->pretty_output)
if (pretty)
trace_seq_printf(s, "\033[2;37;40m");
trace_seq_printf(s, " Timer Latency ");
if (params->user_data)
if (params->common.user_data)
trace_seq_printf(s, " ");
if (params->pretty_output)
if (pretty)
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)", duration,
params->output_divisor == 1 ? "ns" : "us",
params->output_divisor == 1 ? "ns" : "us");
params->common.output_divisor == 1 ? "ns" : "us",
params->common.output_divisor == 1 ? "ns" : "us");
if (params->user_data) {
if (params->common.user_data) {
trace_seq_printf(s, " | Ret user Timer Latency (%s)",
params->output_divisor == 1 ? "ns" : "us");
params->common.output_divisor == 1 ? "ns" : "us");
}
trace_seq_printf(s, "\n");
if (params->pretty_output)
if (pretty)
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_data)
if (params->common.user_data)
trace_seq_printf(s, " | cur min avg max");
if (params->pretty_output)
if (pretty)
trace_seq_printf(s, "\033[0;0;0m");
trace_seq_printf(s, "\n");
}
@ -302,8 +305,7 @@ static const char *no_value = " -";
*/
static void timerlat_top_print(struct osnoise_tool *top, int cpu)
{
struct timerlat_params *params = top->params;
struct timerlat_params *params = to_timerlat_params(top->params);
struct timerlat_top_data *data = top->data;
struct timerlat_top_cpu *cpu_data = &data->cpu_data[cpu];
struct trace_seq *s = top->trace.seq;
@ -338,7 +340,7 @@ static void timerlat_top_print(struct osnoise_tool *top, int cpu)
trace_seq_printf(s, "%9llu", cpu_data->max_thread);
}
if (!params->user_data) {
if (!params->common.user_data) {
trace_seq_printf(s, "\n");
return;
}
@ -363,7 +365,7 @@ static void
timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summary)
{
const char *split = "----------------------------------------";
struct timerlat_params *params = top->params;
struct timerlat_params *params = to_timerlat_params(top->params);
unsigned long long count = summary->irq_count;
struct trace_seq *s = top->trace.seq;
int e = 0;
@ -380,7 +382,7 @@ timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summar
}
trace_seq_printf(s, "%.*s|%.*s|%.*s", 15, split, 40, split, 39, split);
if (params->user_data)
if (params->common.user_data)
trace_seq_printf(s, "-|%.*s", 39, split);
trace_seq_printf(s, "\n");
@ -405,7 +407,7 @@ timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summar
trace_seq_printf(s, "%9llu", summary->max_thread);
}
if (!params->user_data) {
if (!params->common.user_data) {
trace_seq_printf(s, "\n");
return;
}
@ -436,20 +438,21 @@ static void clear_terminal(struct trace_seq *seq)
* timerlat_print_stats - print data for all cpus
*/
static void
timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *top)
timerlat_print_stats(struct osnoise_tool *top)
{
struct timerlat_params *params = to_timerlat_params(top->params);
struct trace_instance *trace = &top->trace;
struct timerlat_top_cpu summary;
static int nr_cpus = -1;
int i;
if (params->aa_only)
if (params->common.aa_only)
return;
if (nr_cpus == -1)
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
if (!params->quiet)
if (!params->common.quiet)
clear_terminal(trace->seq);
timerlat_top_reset_sum(&summary);
@ -457,7 +460,7 @@ timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *top)
timerlat_top_header(params, top);
for (i = 0; i < nr_cpus; i++) {
if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
if (params->common.cpus && !CPU_ISSET(i, &params->common.monitored_cpus))
continue;
timerlat_top_print(top, i);
timerlat_top_update_sum(top, i, &summary);
@ -539,7 +542,7 @@ static void timerlat_top_usage(char *usage)
/*
* timerlat_top_parse_args - allocs, parse and fill the cmd line parameters
*/
static struct timerlat_params
static struct common_params
*timerlat_top_parse_args(int argc, char **argv)
{
struct timerlat_params *params;
@ -553,8 +556,8 @@ static struct timerlat_params
if (!params)
exit(1);
actions_init(&params->threshold_actions);
actions_init(&params->end_actions);
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
/* disabled by default */
params->dma_latency = -1;
@ -563,7 +566,7 @@ static struct timerlat_params
params->deepest_idle_state = -2;
/* display data in microseconds */
params->output_divisor = 1000;
params->common.output_divisor = 1000;
/* default to BPF mode */
params->mode = TRACING_MODE_BPF;
@ -618,8 +621,8 @@ static struct timerlat_params
auto_thresh = get_llong_from_str(optarg);
/* set thread stop to auto_thresh */
params->stop_total_us = auto_thresh;
params->stop_us = auto_thresh;
params->common.stop_total_us = auto_thresh;
params->common.stop_us = auto_thresh;
/* get stack trace */
params->print_stack = auto_thresh;
@ -633,37 +636,37 @@ static struct timerlat_params
auto_thresh = get_llong_from_str(optarg);
/* set thread stop to auto_thresh */
params->stop_total_us = auto_thresh;
params->stop_us = auto_thresh;
params->common.stop_total_us = auto_thresh;
params->common.stop_us = auto_thresh;
/* get stack trace */
params->print_stack = auto_thresh;
/* set aa_only to avoid parsing the trace */
params->aa_only = 1;
params->common.aa_only = 1;
break;
case 'c':
retval = parse_cpu_set(optarg, &params->monitored_cpus);
retval = parse_cpu_set(optarg, &params->common.monitored_cpus);
if (retval)
timerlat_top_usage("\nInvalid -c cpu list\n");
params->cpus = optarg;
params->common.cpus = optarg;
break;
case 'C':
params->cgroup = 1;
params->common.cgroup = 1;
if (!optarg) {
/* will inherit this cgroup */
params->cgroup_name = NULL;
params->common.cgroup_name = NULL;
} else if (*optarg == '=') {
/* skip the = */
params->cgroup_name = ++optarg;
params->common.cgroup_name = ++optarg;
}
break;
case 'D':
config_debug = 1;
break;
case 'd':
params->duration = parse_seconds_duration(optarg);
if (!params->duration)
params->common.duration = parse_seconds_duration(optarg);
if (!params->common.duration)
timerlat_top_usage("Invalid -d duration\n");
break;
case 'e':
@ -673,30 +676,30 @@ static struct timerlat_params
exit(EXIT_FAILURE);
}
if (params->events)
tevent->next = params->events;
params->events = tevent;
if (params->common.events)
tevent->next = params->common.events;
params->common.events = tevent;
break;
case 'h':
case '?':
timerlat_top_usage(NULL);
break;
case 'H':
params->hk_cpus = 1;
retval = parse_cpu_set(optarg, &params->hk_cpu_set);
params->common.hk_cpus = 1;
retval = parse_cpu_set(optarg, &params->common.hk_cpu_set);
if (retval) {
err_msg("Error parsing house keeping CPUs\n");
exit(EXIT_FAILURE);
}
break;
case 'i':
params->stop_us = get_llong_from_str(optarg);
params->common.stop_us = get_llong_from_str(optarg);
break;
case 'k':
params->kernel_workload = true;
params->common.kernel_workload = true;
break;
case 'n':
params->output_divisor = 1;
params->common.output_divisor = 1;
break;
case 'p':
params->timerlat_period_us = get_llong_from_str(optarg);
@ -704,19 +707,19 @@ static struct timerlat_params
timerlat_top_usage("Period longer than 1 s\n");
break;
case 'P':
retval = parse_prio(optarg, &params->sched_param);
retval = parse_prio(optarg, &params->common.sched_param);
if (retval == -1)
timerlat_top_usage("Invalid -P priority");
params->set_sched = 1;
params->common.set_sched = 1;
break;
case 'q':
params->quiet = 1;
params->common.quiet = 1;
break;
case 's':
params->print_stack = get_llong_from_str(optarg);
break;
case 'T':
params->stop_total_us = get_llong_from_str(optarg);
params->common.stop_total_us = get_llong_from_str(optarg);
break;
case 't':
if (optarg) {
@ -730,14 +733,14 @@ static struct timerlat_params
trace_output = "timerlat_trace.txt";
break;
case 'u':
params->user_workload = true;
params->common.user_workload = true;
/* fallback: -u implies -U */
case 'U':
params->user_data = true;
params->common.user_data = true;
break;
case '0': /* trigger */
if (params->events) {
retval = trace_event_add_trigger(params->events, optarg);
if (params->common.events) {
retval = trace_event_add_trigger(params->common.events, optarg);
if (retval) {
err_msg("Error adding trigger %s\n", optarg);
exit(EXIT_FAILURE);
@ -747,8 +750,8 @@ static struct timerlat_params
}
break;
case '1': /* filter */
if (params->events) {
retval = trace_event_add_filter(params->events, optarg);
if (params->common.events) {
retval = trace_event_add_filter(params->common.events, optarg);
if (retval) {
err_msg("Error adding filter %s\n", optarg);
exit(EXIT_FAILURE);
@ -771,23 +774,25 @@ static struct timerlat_params
params->dump_tasks = 1;
break;
case '6':
params->warmup = get_llong_from_str(optarg);
params->common.warmup = get_llong_from_str(optarg);
break;
case '7':
params->buffer_size = get_llong_from_str(optarg);
params->common.buffer_size = get_llong_from_str(optarg);
break;
case '8':
params->deepest_idle_state = get_llong_from_str(optarg);
break;
case '9':
retval = actions_parse(&params->threshold_actions, optarg);
retval = actions_parse(&params->common.threshold_actions, optarg,
"timerlat_trace.txt");
if (retval) {
err_msg("Invalid action %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case '\1':
retval = actions_parse(&params->end_actions, optarg);
retval = actions_parse(&params->common.end_actions, optarg,
"timerlat_trace.txt");
if (retval) {
err_msg("Invalid action %s\n", optarg);
exit(EXIT_FAILURE);
@ -799,7 +804,7 @@ static struct timerlat_params
}
if (trace_output)
actions_add_trace_output(&params->threshold_actions, trace_output);
actions_add_trace_output(&params->common.threshold_actions, trace_output);
if (geteuid()) {
err_msg("rtla needs root permission\n");
@ -809,13 +814,13 @@ static struct timerlat_params
/*
* Auto analysis only happens if stop tracing, thus:
*/
if (!params->stop_us && !params->stop_total_us)
if (!params->common.stop_us && !params->common.stop_total_us)
params->no_aa = 1;
if (params->no_aa && params->aa_only)
if (params->no_aa && params->common.aa_only)
timerlat_top_usage("--no-aa and --aa-only are mutually exclusive!");
if (params->kernel_workload && params->user_workload)
if (params->common.kernel_workload && params->common.user_workload)
timerlat_top_usage("--kernel-threads and --user-threads are mutually exclusive!");
/*
@ -823,27 +828,29 @@ static struct timerlat_params
* mixed mode
*/
if (params->mode == TRACING_MODE_BPF &&
(params->threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->end_actions.present[ACTION_TRACE_OUTPUT] || !params->no_aa))
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
!params->no_aa))
params->mode = TRACING_MODE_MIXED;
return params;
return &params->common;
}
/*
* timerlat_top_apply_config - apply the top configs to the initialized tool
*/
static int
timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_params *params)
timerlat_top_apply_config(struct osnoise_tool *top)
{
struct timerlat_params *params = to_timerlat_params(top->params);
int retval;
retval = timerlat_apply_config(top, params);
if (retval)
goto out_err;
if (isatty(STDOUT_FILENO) && !params->quiet)
params->pretty_output = 1;
if (isatty(STDOUT_FILENO) && !params->common.quiet)
params->common.pretty_output = 1;
return 0;
@ -855,7 +862,7 @@ out_err:
* timerlat_init_top - initialize a timerlat top tool with parameters
*/
static struct osnoise_tool
*timerlat_init_top(struct timerlat_params *params)
*timerlat_init_top(struct common_params *params)
{
struct osnoise_tool *top;
int nr_cpus;
@ -870,8 +877,6 @@ static struct osnoise_tool
if (!top->data)
goto out_err;
top->params = params;
tep_register_event_handler(top->trace.tep, -1, "ftrace", "timerlat",
timerlat_top_handler, top);
@ -882,109 +887,16 @@ out_err:
return NULL;
}
static int stop_tracing;
static struct trace_instance *top_inst = NULL;
static void stop_top(int sig)
{
if (stop_tracing) {
/*
* Stop requested twice in a row; abort event processing and
* exit immediately
*/
tracefs_iterate_stop(top_inst->inst);
return;
}
stop_tracing = 1;
if (top_inst)
trace_instance_stop(top_inst);
}
/*
* timerlat_top_set_signals - handles the signal to stop the tool
*/
static void
timerlat_top_set_signals(struct timerlat_params *params)
{
signal(SIGINT, stop_top);
if (params->duration) {
signal(SIGALRM, stop_top);
alarm(params->duration);
}
}
/*
* timerlat_top_main_loop - main loop to process events
*/
static int
timerlat_top_main_loop(struct osnoise_tool *top,
struct osnoise_tool *record,
struct osnoise_tool *aa,
struct timerlat_params *params,
struct timerlat_u_params *params_u)
{
struct trace_instance *trace = &top->trace;
int retval;
while (!stop_tracing) {
sleep(params->sleep_time);
if (params->aa_only && !osnoise_trace_is_off(top, record))
continue;
retval = tracefs_iterate_raw_events(trace->tep,
trace->inst,
NULL,
0,
collect_registered_events,
trace);
if (retval < 0) {
err_msg("Error iterating on events\n");
return retval;
}
if (!params->quiet)
timerlat_print_stats(params, top);
if (osnoise_trace_is_off(top, record)) {
actions_perform(&params->threshold_actions);
if (!params->threshold_actions.continue_flag)
/* continue flag not set, break */
break;
/* continue action reached, re-enable tracing */
if (record)
trace_instance_start(&record->trace);
if (!params->no_aa)
trace_instance_start(&aa->trace);
trace_instance_start(trace);
}
/* is there still any user-threads ? */
if (params->user_workload) {
if (params_u->stopped_running) {
debug_msg("timerlat user space threads stopped!\n");
break;
}
}
}
return 0;
}
/*
* timerlat_top_bpf_main_loop - main loop to process events (BPF variant)
*/
static int
timerlat_top_bpf_main_loop(struct osnoise_tool *top,
struct osnoise_tool *record,
struct osnoise_tool *aa,
struct timerlat_params *params,
struct timerlat_u_params *params_u)
timerlat_top_bpf_main_loop(struct osnoise_tool *tool)
{
struct timerlat_params *params = to_timerlat_params(tool->params);
int retval, wait_retval;
if (params->aa_only) {
if (params->common.aa_only) {
/* Auto-analysis only, just wait for stop tracing */
timerlat_bpf_wait(-1);
return 0;
@ -992,36 +904,37 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *top,
/* Pull and display data in a loop */
while (!stop_tracing) {
wait_retval = timerlat_bpf_wait(params->quiet ? -1 : params->sleep_time);
wait_retval = timerlat_bpf_wait(params->common.quiet ? -1 :
params->common.sleep_time);
retval = timerlat_top_bpf_pull_data(top);
retval = timerlat_top_bpf_pull_data(tool);
if (retval) {
err_msg("Error pulling BPF data\n");
return retval;
}
if (!params->quiet)
timerlat_print_stats(params, top);
if (!params->common.quiet)
timerlat_print_stats(tool);
if (wait_retval == 1) {
/* Stopping requested by tracer */
actions_perform(&params->threshold_actions);
actions_perform(&params->common.threshold_actions);
if (!params->threshold_actions.continue_flag)
if (!params->common.threshold_actions.continue_flag)
/* continue flag not set, break */
break;
/* continue action reached, re-enable tracing */
if (record)
trace_instance_start(&record->trace);
if (!params->no_aa)
trace_instance_start(&aa->trace);
if (tool->record)
trace_instance_start(&tool->record->trace);
if (tool->aa)
trace_instance_start(&tool->aa->trace);
timerlat_bpf_restart_tracing();
}
/* is there still any user-threads ? */
if (params->user_workload) {
if (params_u->stopped_running) {
if (params->common.user_workload) {
if (params->common.user.stopped_running) {
debug_msg("timerlat user space threads stopped!\n");
break;
}
@ -1031,273 +944,30 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *top,
return 0;
}
int timerlat_top_main(int argc, char *argv[])
static int timerlat_top_main_loop(struct osnoise_tool *tool)
{
struct timerlat_params *params;
struct osnoise_tool *record = NULL;
struct timerlat_u_params params_u;
enum result return_value = ERROR;
struct osnoise_tool *top = NULL;
struct osnoise_tool *aa = NULL;
struct trace_instance *trace;
int dma_latency_fd = -1;
pthread_t timerlat_u;
char *max_lat;
struct timerlat_params *params = to_timerlat_params(tool->params);
int retval;
int nr_cpus, i;
params = timerlat_top_parse_args(argc, argv);
if (!params)
exit(1);
top = timerlat_init_top(params);
if (!top) {
err_msg("Could not init osnoise top\n");
goto out_exit;
}
trace = &top->trace;
/*
* Save trace instance into global variable so that SIGINT can stop
* the timerlat tracer.
* Otherwise, rtla could loop indefinitely when overloaded.
*/
top_inst = trace;
/*
* Try to enable BPF, unless disabled explicitly.
* If BPF enablement fails, fall back to tracefs mode.
*/
if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) {
debug_msg("RTLA_NO_BPF set, disabling BPF\n");
params->mode = TRACING_MODE_TRACEFS;
} else if (!tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) {
debug_msg("osnoise:timerlat_sample missing, disabling BPF\n");
params->mode = TRACING_MODE_TRACEFS;
} else {
retval = timerlat_bpf_init(params);
if (retval) {
debug_msg("Could not enable BPF\n");
params->mode = TRACING_MODE_TRACEFS;
}
}
retval = timerlat_top_apply_config(top, params);
if (retval) {
err_msg("Could not apply config\n");
goto out_free;
}
retval = enable_timerlat(trace);
if (retval) {
err_msg("Failed to enable timerlat tracer\n");
goto out_free;
}
if (params->set_sched) {
retval = set_comm_sched_attr("timerlat/", &params->sched_param);
if (retval) {
err_msg("Failed to set sched parameters\n");
goto out_free;
}
}
if (params->cgroup && !params->user_data) {
retval = set_comm_cgroup("timerlat/", params->cgroup_name);
if (!retval) {
err_msg("Failed to move threads to cgroup\n");
goto out_free;
}
}
if (params->dma_latency >= 0) {
dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
if (dma_latency_fd < 0) {
err_msg("Could not set /dev/cpu_dma_latency.\n");
goto out_free;
}
}
if (params->deepest_idle_state >= -1) {
if (!have_libcpupower_support()) {
err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n");
goto out_free;
}
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
for (i = 0; i < nr_cpus; i++) {
if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
continue;
if (save_cpu_idle_disable_state(i) < 0) {
err_msg("Could not save cpu idle state.\n");
goto out_free;
}
if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) {
err_msg("Could not set deepest cpu idle state.\n");
goto out_free;
}
}
}
if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->end_actions.present[ACTION_TRACE_OUTPUT]) {
record = osnoise_init_trace_tool("timerlat");
if (!record) {
err_msg("Failed to enable the trace instance\n");
goto out_free;
}
params->threshold_actions.trace_output_inst = record->trace.inst;
params->end_actions.trace_output_inst = record->trace.inst;
if (params->events) {
retval = trace_events_enable(&record->trace, params->events);
if (retval)
goto out_top;
}
if (params->buffer_size > 0) {
retval = trace_set_buffer_size(&record->trace, params->buffer_size);
if (retval)
goto out_top;
}
}
if (!params->no_aa) {
aa = osnoise_init_tool("timerlat_aa");
if (!aa)
goto out_top;
retval = timerlat_aa_init(aa, params->dump_tasks);
if (retval) {
err_msg("Failed to enable the auto analysis instance\n");
goto out_top;
}
/* if it is re-using the main instance, there is no need to start it */
if (aa != top) {
retval = enable_timerlat(&aa->trace);
if (retval) {
err_msg("Failed to enable timerlat tracer\n");
goto out_top;
}
}
}
if (params->user_workload) {
/* 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");
}
if (params->warmup > 0) {
debug_msg("Warming up for %d seconds\n", params->warmup);
sleep(params->warmup);
}
/*
* Start the tracers here, after having set all instances.
*
* Let the trace instance start first for the case of hitting a stop
* tracing while enabling other instances. The trace instance is the
* one with most valuable information.
*/
if (record)
trace_instance_start(&record->trace);
if (!params->no_aa)
trace_instance_start(&aa->trace);
if (params->mode == TRACING_MODE_TRACEFS) {
trace_instance_start(trace);
retval = top_main_loop(tool);
} else {
retval = timerlat_bpf_attach();
if (retval) {
err_msg("Error attaching BPF program\n");
goto out_top;
}
}
top->start_time = time(NULL);
timerlat_top_set_signals(params);
if (params->mode == TRACING_MODE_TRACEFS)
retval = timerlat_top_main_loop(top, record, aa, params, &params_u);
else
retval = timerlat_top_bpf_main_loop(top, record, aa, params, &params_u);
if (retval)
goto out_top;
if (params->mode != TRACING_MODE_TRACEFS)
retval = timerlat_top_bpf_main_loop(tool);
timerlat_bpf_detach();
if (params->user_workload && !params_u.stopped_running) {
params_u.should_run = 0;
sleep(1);
}
timerlat_print_stats(params, top);
actions_perform(&params->end_actions);
return_value = PASSED;
if (osnoise_trace_is_off(top, record) && !stop_tracing) {
printf("rtla timerlat hit stop tracing\n");
if (!params->no_aa)
timerlat_auto_analysis(params->stop_us, params->stop_total_us);
return_value = FAILED;
} else if (params->aa_only) {
/*
* If the trace did not stop with --aa-only, at least print the
* max known latency.
*/
max_lat = tracefs_instance_file_read(trace->inst, "tracing_max_latency", NULL);
if (max_lat) {
printf(" Max latency was %s\n", max_lat);
free(max_lat);
}
}
out_top:
timerlat_aa_destroy();
if (dma_latency_fd >= 0)
close(dma_latency_fd);
if (params->deepest_idle_state >= -1) {
for (i = 0; i < nr_cpus; i++) {
if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
continue;
restore_cpu_idle_disable_state(i);
}
}
trace_events_destroy(&record->trace, params->events);
params->events = NULL;
out_free:
timerlat_free_top(top->data);
if (aa && aa != top)
osnoise_destroy_tool(aa);
osnoise_destroy_tool(record);
osnoise_destroy_tool(top);
actions_destroy(&params->threshold_actions);
actions_destroy(&params->end_actions);
if (params->mode != TRACING_MODE_TRACEFS)
timerlat_bpf_destroy();
free(params);
free_cpu_idle_disable_states();
out_exit:
exit(return_value);
return retval;
}
struct tool_ops timerlat_top_ops = {
.tracer = "timerlat",
.comm_prefix = "timerlat/",
.parse_args = timerlat_top_parse_args,
.init_tool = timerlat_init_top,
.apply_config = timerlat_top_apply_config,
.enable = timerlat_enable,
.main = timerlat_top_main_loop,
.print_stats = timerlat_print_stats,
.analyze = timerlat_analyze,
.free = timerlat_free_top_tool,
};

View File

@ -30,9 +30,6 @@ struct trace_seq *get_trace_seq(void);
int enable_tracer_by_name(struct tracefs_instance *inst, const char *tracer_name);
void disable_tracer(struct tracefs_instance *inst);
int enable_osnoise(struct trace_instance *trace);
int enable_timerlat(struct trace_instance *trace);
struct tracefs_instance *create_instance(char *instance_name);
void destroy_instance(struct tracefs_instance *inst);

View File

@ -43,6 +43,7 @@ check() {
tested_command=$1
expected_exitcode=${3:-0}
expected_output=$4
unexpected_output=$5
# Simple check: run rtla with given arguments and test exit code.
# If TEST_COUNT is set, run the test. Otherwise, just count.
ctr=$(($ctr + 1))
@ -53,24 +54,33 @@ check() {
# Run rtla; in case of failure, include its output as comment
# in the test results.
result=$(eval stdbuf -oL $TIMEOUT "$RTLA" $2 2>&1); exitcode=$?
failbuf=''
fail=0
# Test if the results matches if requested
if [ -n "$expected_output" ]
if [ -n "$expected_output" ] && ! grep -qE "$expected_output" <<< "$result"
then
grep -E "$expected_output" <<< "$result" > /dev/null; grep_result=$?
else
grep_result=0
fail=1
failbuf+=$(printf "# Output match failed: \"%s\"" "$expected_output")
failbuf+=$'\n'
fi
if [ $exitcode -eq $expected_exitcode ] && [ $grep_result -eq 0 ]
if [ -n "$unexpected_output" ] && grep -qE "$unexpected_output" <<< "$result"
then
fail=1
failbuf+=$(printf "# Output non-match failed: \"%s\"" "$unexpected_output")
failbuf+=$'\n'
fi
if [ $exitcode -eq $expected_exitcode ] && [ $fail -eq 0 ]
then
echo "ok $ctr - $1"
else
echo "not ok $ctr - $1"
# Add rtla output and exit code as comments in case of failure
echo "not ok $ctr - $1"
echo -n "$failbuf"
echo "$result" | col -b | while read line; do echo "# $line"; done
printf "#\n# exit code %s\n" $exitcode
[ -n "$expected_output" ] && [ $grep_result -ne 0 ] && \
printf "# Output match failed: \"%s\"\n" "$expected_output"
fi
fi
}

View File

@ -8,7 +8,8 @@ set_timeout 2m
check "verify help page" \
"osnoise --help" 0 "osnoise version"
check "verify the --priority/-P param" \
"osnoise top -P F:1 -c 0 -r 900000 -d 10s -q"
"osnoise top -P F:1 -c 0 -r 900000 -d 10s -q -S 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh osnoise/ SCHED_FIFO 1\"" \
2 "Priorities are set correctly"
check "verify the --stop/-s param" \
"osnoise top -s 30 -T 1" 2 "osnoise hit stop tracing"
check "verify the --trace param" \
@ -22,4 +23,28 @@ check "verify the --entries/-E param" \
check_with_osnoise_options "apply default period" \
"osnoise hist -s 1" 2 period_us=600000000
# Actions tests
check "trace output through -t with custom filename" \
"osnoise hist -S 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check "trace output through --on-threshold trace" \
"osnoise hist -S 2 --on-threshold trace" 2 "^ Saving trace to osnoise_trace.txt$"
check "trace output through --on-threshold trace with custom filename" \
"osnoise hist -S 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check "exec command" \
"osnoise hist -S 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
check "multiple actions" \
"osnoise hist -S 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
check "hist stop at failed action" \
"osnoise hist -S 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA osnoise histogram$"
check "top stop at failed action" \
"timerlat top -T 2 --on-threshold shell,command='echo -n abc; false' --on-threshold shell,command='echo -n defgh'" 2 "^abc" "defgh"
check "hist with continue" \
"osnoise hist -S 2 -d 1s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
check "top with continue" \
"osnoise top -q -S 2 -d 1s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
check "hist with trace output at end" \
"osnoise hist -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$"
check "top with trace output at end" \
"osnoise top -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$"
test_end

View File

@ -47,9 +47,9 @@ check "trace output through -t" \
"timerlat hist -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$"
check "trace output through -t with custom filename" \
"timerlat hist -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check "trace output through -A trace" \
check "trace output through --on-threshold trace" \
"timerlat hist -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$"
check "trace output through -A trace with custom filename" \
check "trace output through --on-threshold trace with custom filename" \
"timerlat hist -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check "exec command" \
"timerlat hist -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"