// SPDX-License-Identifier: GPL-2.0#define _GNU_SOURCE #include <pthread.h>#include <signal.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/sysinfo.h> #include "common.h" struct osnoise_tool *trace_tool;volatile int stop_tracing;int nr_cpus; static void stop_trace(int sig){ if (stop_tracing) { /* * Stop requested twice in a row; abort event processing and * exit immediately */ if (trace_tool) tracefs_iterate_stop(trace_tool->trace.inst); return; } stop_tracing = 1; if (trace_tool) { trace_instance_stop(&trace_tool->trace); if (trace_tool->record) trace_instance_stop(&trace_tool->record->trace); }} /* * 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); }} /* * unset_signals - unsets the signals to stop the tool */static void unset_signals(struct common_params *params){ signal(SIGINT, SIG_DFL); if (params->duration) { alarm(0); signal(SIGALRM, SIG_DFL); }} /* * common_apply_config - apply common configs to the initialized tool */intcommon_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 < nr_cpus; 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;} /** * common_threshold_handler - handle latency threshold overflow * @tool: pointer to the osnoise_tool instance containing trace contexts * * Executes the configured threshold actions (e.g., saving trace, printing, * sending signals). If the continue flag is set (--on-threshold continue), * restarts the auxiliary trace instances to continue monitoring. * * Return: 0 for success, -1 for error. */intcommon_threshold_handler(const struct osnoise_tool *tool){ actions_perform(&tool->params->threshold_actions); if (!should_continue_tracing(tool->params)) /* continue flag not set, break */ return 0; /* continue action reached, re-enable tracing */ if (tool->record && trace_instance_start(&tool->record->trace)) goto err; if (tool->aa && trace_instance_start(&tool->aa->trace)) goto err; return 0; err: err_msg("Error restarting trace\n"); 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; nr_cpus = get_nprocs_conf(); 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; /* * Expose the tool to signal handlers so they can stop the trace. * Otherwise, rtla could loop indefinitely when overloaded. */ trace_tool = tool; retval = ops->apply_config(tool); if (retval) { err_msg("Could not apply config\n"); goto out_free; } retval = enable_tracer_by_name(tool->trace.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"); goto out_trace; } } 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_signals; 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_signals: unset_signals(params);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)) { if (stop_tracing) /* stop tracing requested, do not perform actions */ return 0; retval = common_threshold_handler(tool); if (retval) return retval; if (!should_continue_tracing(params)) return 0; 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)) { if (stop_tracing) /* stop tracing requested, do not perform actions */ break; retval = common_threshold_handler(tool); if (retval) return retval; if (!should_continue_tracing(params)) return 0; trace_instance_start(trace); } /* 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;} int osn_set_stop(struct osnoise_tool *tool){ struct common_params *params = tool->params; int retval; retval = osnoise_set_stop_us(tool->context, params->stop_us); if (retval) { err_msg("Failed to set stop us\n"); return retval; } retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); if (retval) { err_msg("Failed to set stop total us\n"); return retval; } return 0;} static void print_msg_array(const char * const *msgs){ if (!msgs) return; for (int i = 0; msgs[i]; i++) fprintf(stderr, "%s\n", msgs[i]);} /* * common_usage - print complete usage information */void common_usage(const char *tool, const char *mode, const char *desc, const char * const *start_msgs, const char * const *opt_msgs){ static const char * const common_options[] = { " -h/--help: print this menu", NULL }; fprintf(stderr, "rtla %s", tool); if (strcmp(mode, "")) fprintf(stderr, " %s", mode); fprintf(stderr, ": %s (version %s)\n\n", desc, VERSION); fprintf(stderr, " usage: [rtla] %s ", tool); if (strcmp(mode, "top") == 0) fprintf(stderr, "[top] [-h] "); else fprintf(stderr, "%s [-h] ", mode); print_msg_array(start_msgs); fprintf(stderr, "\n"); print_msg_array(common_options); print_msg_array(opt_msgs); exit(EXIT_SUCCESS);}
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include "common.h"
struct osnoise_tool *trace_tool;
volatile int stop_tracing;
int nr_cpus;
static void stop_trace(int sig)
{
if (stop_tracing) {
/*
* Stop requested twice in a row; abort event processing and
* exit immediately
*/
if (trace_tool)
tracefs_iterate_stop(trace_tool->trace.inst);
return;
}
stop_tracing = 1;
if (trace_tool) {
trace_instance_stop(&trace_tool->trace);
if (trace_tool->record)
trace_instance_stop(&trace_tool->record->trace);
}
}
/*
* 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);
}
}
/*
* unset_signals - unsets the signals to stop the tool
*/
static void unset_signals(struct common_params *params)
{
signal(SIGINT, SIG_DFL);
if (params->duration) {
alarm(0);
signal(SIGALRM, SIG_DFL);
}
}
/*
* 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 < nr_cpus; 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;
}
/**
* common_threshold_handler - handle latency threshold overflow
* @tool: pointer to the osnoise_tool instance containing trace contexts
*
* Executes the configured threshold actions (e.g., saving trace, printing,
* sending signals). If the continue flag is set (--on-threshold continue),
* restarts the auxiliary trace instances to continue monitoring.
*
* Return: 0 for success, -1 for error.
*/
int
common_threshold_handler(const struct osnoise_tool *tool)
{
actions_perform(&tool->params->threshold_actions);
if (!should_continue_tracing(tool->params))
/* continue flag not set, break */
return 0;
/* continue action reached, re-enable tracing */
if (tool->record && trace_instance_start(&tool->record->trace))
goto err;
if (tool->aa && trace_instance_start(&tool->aa->trace))
goto err;
return 0;
err:
err_msg("Error restarting trace\n");
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;
nr_cpus = get_nprocs_conf();
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;
/*
* Expose the tool to signal handlers so they can stop the trace.
* Otherwise, rtla could loop indefinitely when overloaded.
*/
trace_tool = tool;
retval = ops->apply_config(tool);
if (retval) {
err_msg("Could not apply config\n");
goto out_free;
}
retval = enable_tracer_by_name(tool->trace.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");
goto out_trace;
}
}
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_signals;
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_signals:
unset_signals(params);
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)) {
if (stop_tracing)
/* stop tracing requested, do not perform actions */
return 0;
retval = common_threshold_handler(tool);
if (retval)
return retval;
if (!should_continue_tracing(params))
return 0;
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)) {
if (stop_tracing)
/* stop tracing requested, do not perform actions */
break;
retval = common_threshold_handler(tool);
if (retval)
return retval;
if (!should_continue_tracing(params))
return 0;
trace_instance_start(trace);
}
/* 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;
}
int osn_set_stop(struct osnoise_tool *tool)
{
struct common_params *params = tool->params;
int retval;
retval = osnoise_set_stop_us(tool->context, params->stop_us);
if (retval) {
err_msg("Failed to set stop us\n");
return retval;
}
retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
if (retval) {
err_msg("Failed to set stop total us\n");
return retval;
}
return 0;
}
static void print_msg_array(const char * const *msgs)
{
if (!msgs)
return;
for (int i = 0; msgs[i]; i++)
fprintf(stderr, "%s\n", msgs[i]);
}
/*
* common_usage - print complete usage information
*/
void common_usage(const char *tool, const char *mode,
const char *desc, const char * const *start_msgs, const char * const *opt_msgs)
{
static const char * const common_options[] = {
" -h/--help: print this menu",
NULL
};
fprintf(stderr, "rtla %s", tool);
if (strcmp(mode, ""))
fprintf(stderr, " %s", mode);
fprintf(stderr, ": %s (version %s)\n\n", desc, VERSION);
fprintf(stderr, " usage: [rtla] %s ", tool);
if (strcmp(mode, "top") == 0)
fprintf(stderr, "[top] [-h] ");
else
fprintf(stderr, "%s [-h] ", mode);
print_msg_array(start_msgs);
fprintf(stderr, "\n");
print_msg_array(common_options);
print_msg_array(opt_msgs);
exit(EXIT_SUCCESS);
}