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:
commit
d9f24f8e60
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. |tool| replace:: hwnoise
|
||||
|
||||
============
|
||||
rtla-hwnoise
|
||||
============
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. |tool| replace:: osnoise hist
|
||||
|
||||
===================
|
||||
rtla-osnoise-hist
|
||||
===================
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. |tool| replace:: osnoise top
|
||||
|
||||
===================
|
||||
rtla-osnoise-top
|
||||
===================
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. |tool| replace:: timerlat hist
|
||||
|
||||
=====================
|
||||
rtla-timerlat-hist
|
||||
=====================
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. |tool| replace:: timerlat top
|
||||
|
||||
====================
|
||||
rtla-timerlat-top
|
||||
====================
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, ¶ms->monitored_cpus);
|
||||
}
|
||||
|
||||
if (params->hk_cpus) {
|
||||
retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
|
||||
¶ms->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(¶ms->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, ¶ms->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 = ¶ms->monitored_cpus;
|
||||
if (params->set_sched)
|
||||
params->user.sched_param = ¶ms->sched_param;
|
||||
else
|
||||
params->user.sched_param = NULL;
|
||||
|
||||
params->user.cgroup_name = params->cgroup_name;
|
||||
|
||||
retval = pthread_create(&user_thread, NULL, timerlat_u_dispatcher, ¶ms->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(¶ms->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(¶ms->threshold_actions);
|
||||
actions_destroy(¶ms->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(¶ms->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(¶ms->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;
|
||||
}
|
|
@ -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);
|
|
@ -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),
|
||||
¶ms->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(¶ms->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, ¶ms->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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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, ¶ms->monitored_cpus);
|
||||
retval = parse_cpu_set(optarg, ¶ms->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, ¶ms->hk_cpu_set);
|
||||
params->common.hk_cpus = 1;
|
||||
retval = parse_cpu_set(optarg, ¶ms->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, ¶ms->sched_param);
|
||||
retval = parse_prio(optarg, ¶ms->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(¶ms->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(¶ms->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(¶ms->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 ¶ms->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/", ¶ms->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,
|
||||
};
|
||||
|
|
|
@ -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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(i, ¶ms->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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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, ¶ms->monitored_cpus);
|
||||
retval = parse_cpu_set(optarg, ¶ms->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, ¶ms->hk_cpu_set);
|
||||
params->common.hk_cpus = 1;
|
||||
retval = parse_cpu_set(optarg, ¶ms->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, ¶ms->sched_param);
|
||||
retval = parse_prio(optarg, ¶ms->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(¶ms->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(¶ms->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(¶ms->common.threshold_actions, trace_output);
|
||||
|
||||
if (geteuid()) {
|
||||
err_msg("osnoise needs root permission\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return params;
|
||||
return ¶ms->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/", ¶ms->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,
|
||||
};
|
||||
|
|
|
@ -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, ¶ms->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),
|
||||
¶ms->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(¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
@ -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, ¶ms->monitored_cpus))
|
||||
if (params->common.cpus && !CPU_ISSET(i, ¶ms->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(¶ms->threshold_actions);
|
||||
actions_init(¶ms->end_actions);
|
||||
actions_init(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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, ¶ms->monitored_cpus);
|
||||
retval = parse_cpu_set(optarg, ¶ms->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, ¶ms->hk_cpu_set);
|
||||
params->common.hk_cpus = 1;
|
||||
retval = parse_cpu_set(optarg, ¶ms->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, ¶ms->sched_param);
|
||||
retval = parse_prio(optarg, ¶ms->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(¶ms->threshold_actions, optarg);
|
||||
retval = actions_parse(¶ms->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(¶ms->end_actions, optarg);
|
||||
retval = actions_parse(¶ms->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(¶ms->threshold_actions, trace_output);
|
||||
actions_add_trace_output(¶ms->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 ¶ms->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(¶ms->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(¶ms->threshold_actions);
|
||||
actions_perform(¶ms->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/", ¶ms->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, ¶ms->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 = ¶ms->monitored_cpus;
|
||||
if (params->set_sched)
|
||||
params_u.sched_param = ¶ms->sched_param;
|
||||
else
|
||||
params_u.sched_param = NULL;
|
||||
|
||||
params_u.cgroup_name = params->cgroup_name;
|
||||
|
||||
retval = pthread_create(&timerlat_u, NULL, timerlat_u_dispatcher, ¶ms_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, ¶ms_u);
|
||||
else
|
||||
retval = timerlat_top_bpf_main_loop(top, record, aa, params, ¶ms_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(¶ms->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, ¶ms->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(¶ms->threshold_actions);
|
||||
actions_destroy(¶ms->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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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$"
|
||||
|
|
Loading…
Reference in New Issue