struct timerlat_hist_data { struct timerlat_hist_cpu *hist; int entries; int bucket_size; int nr_cpus;
};
/* * timerlat_free_histogram - free runtime data
*/ staticvoid
timerlat_free_histogram(struct timerlat_hist_data *data)
{ int cpu;
/* one histogram for IRQ and one for thread, per CPU */ for (cpu = 0; cpu < data->nr_cpus; cpu++) { if (data->hist[cpu].irq)
free(data->hist[cpu].irq);
if (data->hist[cpu].thread)
free(data->hist[cpu].thread);
if (data->hist[cpu].user)
free(data->hist[cpu].user);
}
/* one set of histograms per CPU */ if (data->hist)
free(data->hist);
free(data);
}
/* * timerlat_alloc_histogram - alloc runtime data
*/ staticstruct timerlat_hist_data
*timerlat_alloc_histogram(int nr_cpus, int entries, int bucket_size)
{ struct timerlat_hist_data *data; int cpu;
data = calloc(1, sizeof(*data)); if (!data) return NULL;
/* one set of histograms per CPU */
data->hist = calloc(1, sizeof(*data->hist) * nr_cpus); if (!data->hist) goto cleanup;
/* one histogram for IRQ and one for thread, per cpu */ for (cpu = 0; cpu < nr_cpus; cpu++) {
data->hist[cpu].irq = calloc(1, sizeof(*data->hist->irq) * (entries + 1)); if (!data->hist[cpu].irq) goto cleanup;
/* set the min to max */ for (cpu = 0; cpu < nr_cpus; cpu++) {
data->hist[cpu].min_irq = ~0;
data->hist[cpu].min_thread = ~0;
data->hist[cpu].min_user = ~0;
}
/* * timerlat_hist_update - record a new timerlat occurent on cpu, updating data
*/ staticvoid
timerlat_hist_update(struct osnoise_tool *tool, int cpu, unsignedlonglong context, unsignedlonglong latency)
{ struct timerlat_params *params = tool->params; struct timerlat_hist_data *data = tool->data; int entries = data->entries; int bucket; int *hist;
if (params->output_divisor)
latency = latency / params->output_divisor;
if (!params->no_index)
trace_seq_printf(s, "Index");
for (cpu = 0; cpu < data->nr_cpus; cpu++) { if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) continue;
if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue;
if (!params->no_irq)
trace_seq_printf(s, " IRQ-%03d", cpu);
if (!params->no_thread)
trace_seq_printf(s, " Thr-%03d", cpu);
if (params->user_data)
trace_seq_printf(s, " Usr-%03d", cpu);
}
trace_seq_printf(s, "\n");
trace_seq_do_printf(s);
trace_seq_reset(s);
}
/* * format_summary_value - format a line of summary value (min, max or avg) * of hist data
*/ staticvoid format_summary_value(struct trace_seq *seq, int count, unsignedlonglong val, bool avg)
{ if (count)
trace_seq_printf(seq, "%9llu ", avg ? val / count : val); else
trace_seq_printf(seq, "%9c ", '-');
}
/* * timerlat_print_summary - print the summary of the hist data to the output
*/ staticvoid
timerlat_print_summary(struct timerlat_params *params, struct trace_instance *trace, struct timerlat_hist_data *data)
{ int cpu;
if (params->no_summary) return;
if (!params->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)) continue;
if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue;
if (!params->no_irq)
trace_seq_printf(trace->seq, "%9llu ",
data->hist[cpu].irq_count);
if (!params->no_thread)
trace_seq_printf(trace->seq, "%9llu ",
data->hist[cpu].thread_count);
if (params->user_data)
trace_seq_printf(trace->seq, "%9llu ",
data->hist[cpu].user_count);
}
trace_seq_printf(trace->seq, "\n");
if (!params->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)) continue;
if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue;
if (!params->no_irq)
format_summary_value(trace->seq,
data->hist[cpu].irq_count,
data->hist[cpu].min_irq, false);
if (!params->no_thread)
format_summary_value(trace->seq,
data->hist[cpu].thread_count,
data->hist[cpu].min_thread, false);
if (params->user_data)
format_summary_value(trace->seq,
data->hist[cpu].user_count,
data->hist[cpu].min_user, false);
}
trace_seq_printf(trace->seq, "\n");
if (!params->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)) continue;
if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue;
if (!params->no_irq)
format_summary_value(trace->seq,
data->hist[cpu].irq_count,
data->hist[cpu].sum_irq, true);
if (!params->no_thread)
format_summary_value(trace->seq,
data->hist[cpu].thread_count,
data->hist[cpu].sum_thread, true);
if (params->user_data)
format_summary_value(trace->seq,
data->hist[cpu].user_count,
data->hist[cpu].sum_user, true);
}
trace_seq_printf(trace->seq, "\n");
if (!params->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)) continue;
if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue;
if (!params->no_irq)
format_summary_value(trace->seq,
data->hist[cpu].irq_count,
data->hist[cpu].max_irq, false);
if (!params->no_thread)
format_summary_value(trace->seq,
data->hist[cpu].thread_count,
data->hist[cpu].max_thread, false);
/* * timerlat_hist_usage - prints timerlat top usage message
*/ staticvoid timerlat_hist_usage(char *usage)
{ int i;
char *msg[] = { "", " usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", " [-t[file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\", " [--no-index] [--with-zeros] [--dma-latency us] [-C[=cgroup_name]] [--no-aa] [--dump-task] [-u|-k]", " [--warm-up s] [--deepest-idle-state n]", "", " -h/--help: print this menu", " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", " -p/--period us: timerlat period in us", " -i/--irq us: stop trace if the irq latency is higher than the argument in us", " -T/--thread us: stop trace if the thread latency is higher than the argument in us", " -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us", " -c/--cpus cpus: run the tracer only on the given cpus", " -H/--house-keeping cpus: run rtla control threads only on the given cpus", " -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", " -d/--duration time[m|h|d]: duration of the session in seconds", " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)", " -D/--debug: print debug info", " -t/--trace[file]: save the stopped trace to [file|timerlat_trace.txt]", " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", " --filter <filter>: enable a trace event filter to the previous -e event", " --trigger <trigger>: enable a trace event trigger to the previous -e event", " -n/--nano: display data in nanoseconds", " --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage", " -b/--bucket-size N: set the histogram bucket size (default 1)", " -E/--entries N: set the number of entries of the histogram (default 256)", " --no-irq: ignore IRQ latencies", " --no-thread: ignore thread latencies", " --no-header: do not print header", " --no-summary: do not print summary", " --no-index: do not print index", " --with-zeros: print zero only entries", " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", " o:prio - use SCHED_OTHER with prio", " r:prio - use SCHED_RR with prio", " f:prio - use SCHED_FIFO with prio", " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", " in nanoseconds", " -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads", " -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads", " -U/--user-load: enable timerlat for user-defined user-space workload", " --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", " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", " --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed", " --on-end <action>: define action to be executed at measurement end, multiple are allowed",
NULL,
};
if (usage)
fprintf(stderr, "%s\n", usage);
fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n",
VERSION);
for (i = 0; msg[i]; i++)
fprintf(stderr, "%s\n", msg[i]);
if (usage) exit(EXIT_FAILURE);
exit(EXIT_SUCCESS);
}
/* * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters
*/ staticstruct timerlat_params
*timerlat_hist_parse_args(int argc, char *argv[])
{ struct timerlat_params *params; struct trace_events *tevent; int auto_thresh; int retval; int c; char *trace_output = NULL;
params = calloc(1, sizeof(*params)); if (!params) exit(1);
staticint stop_tracing; staticstruct trace_instance *hist_inst = NULL; staticvoid stop_hist(int sig)
{ if (stop_tracing) { /* * Stop requested twice in a row; abort event processing and * exit immediately
*/
tracefs_iterate_stop(hist_inst->inst); return;
}
stop_tracing = 1; if (hist_inst)
trace_instance_stop(hist_inst);
}
/* * timerlat_hist_set_signals - handles the signal to stop the tool
*/ staticvoid
timerlat_hist_set_signals(struct timerlat_params *params)
{
signal(SIGINT, stop_hist); if (params->duration) {
signal(SIGALRM, stop_hist);
alarm(params->duration);
}
}
int timerlat_hist_main(int argc, char *argv[])
{ struct timerlat_params *params; struct osnoise_tool *record = NULL; struct timerlat_u_params params_u; enum result return_value = ERROR; struct osnoise_tool *tool = NULL; struct osnoise_tool *aa = NULL; struct trace_instance *trace; int dma_latency_fd = -1;
pthread_t timerlat_u; int retval; int nr_cpus, i;
params = timerlat_hist_parse_args(argc, argv); if (!params) exit(1);
tool = timerlat_init_hist(params); if (!tool) {
err_msg("Could not init osnoise hist\n"); goto out_exit;
}
trace = &tool->trace; /* * Save trace instance into global variable so that SIGINT can stop * the timerlat tracer. * Otherwise, rtla could loop indefinitely when overloaded.
*/
hist_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;
} elseif (!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_hist_apply_config(tool, 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_workload) {
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_hist;
}
if (params->buffer_size > 0) {
retval = trace_set_buffer_size(&record->trace, params->buffer_size); if (retval) goto out_hist;
}
}
if (!params->no_aa) {
aa = osnoise_init_tool("timerlat_aa"); if (!aa) goto out_hist;
retval = timerlat_aa_init(aa, params->dump_tasks); if (retval) {
err_msg("Failed to enable the auto analysis instance\n"); goto out_hist;
}
retval = enable_timerlat(&aa->trace); if (retval) {
err_msg("Failed to enable timerlat tracer\n"); goto out_hist;
}
}
if (params->user_workload) { /* rtla asked to stop */
params_u.should_run = 1; /* all threads left */
params_u.stopped_running = 0;
if (params->warmup > 0) {
debug_msg("Warming up for %d seconds\n", params->warmup);
sleep(params->warmup); if (stop_tracing) goto out_hist;
}
/* * 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);
} else {
retval = timerlat_bpf_attach(); if (retval) {
err_msg("Error attaching BPF program\n"); goto out_hist;
}
}
if (params->mode == TRACING_MODE_TRACEFS) { 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)) {
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;
}
}
}
} else { while (!stop_tracing) {
timerlat_bpf_wait(-1);
if (!stop_tracing) { /* Threshold overflow, perform actions on threshold */
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);
timerlat_bpf_restart_tracing();
}
}
}
if (params->mode != TRACING_MODE_TRACEFS) {
timerlat_bpf_detach();
retval = timerlat_hist_bpf_pull_data(tool); if (retval) {
err_msg("Error pulling BPF data\n"); goto out_hist;
}
}
if (params->user_workload && !params_u.stopped_running) {
params_u.should_run = 0;
sleep(1);
}
timerlat_print_stats(params, tool);
actions_perform(¶ms->end_actions);
return_value = PASSED;
if (osnoise_trace_is_off(tool, 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;
}
out_hist:
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_histogram(tool->data);
osnoise_destroy_tool(aa);
osnoise_destroy_tool(record);
osnoise_destroy_tool(tool);
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);
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.59 Sekunden
(vorverarbeitet am 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.