/* * timerlat_alloc_histogram - alloc runtime data
*/ staticstruct timerlat_top_data *timerlat_alloc_top(int nr_cpus)
{ struct timerlat_top_data *data; int cpu;
data = calloc(1, sizeof(*data)); if (!data) return NULL;
data->nr_cpus = nr_cpus;
/* one set of histograms per CPU */
data->cpu_data = calloc(1, sizeof(*data->cpu_data) * nr_cpus); if (!data->cpu_data) goto cleanup;
/* set the min to max */ for (cpu = 0; cpu < nr_cpus; cpu++) {
data->cpu_data[cpu].min_irq = ~0;
data->cpu_data[cpu].min_thread = ~0;
data->cpu_data[cpu].min_user = ~0;
}
/* * clear_terminal - clears the output terminal
*/ staticvoid clear_terminal(struct trace_seq *seq)
{ if (!config_debug)
trace_seq_printf(seq, "\033c");
}
/* * timerlat_print_stats - print data for all cpus
*/ staticvoid
timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *top)
{ struct trace_instance *trace = &top->trace; struct timerlat_top_cpu summary; staticint nr_cpus = -1; int i;
if (params->aa_only) return;
if (nr_cpus == -1)
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
if (!params->quiet)
clear_terminal(trace->seq);
timerlat_top_reset_sum(&summary);
timerlat_top_header(params, top);
for (i = 0; i < nr_cpus; i++) { if (params->cpus && !CPU_ISSET(i, ¶ms->monitored_cpus)) continue;
timerlat_top_print(top, i);
timerlat_top_update_sum(top, i, &summary);
}
/* * timerlat_top_usage - prints timerlat top usage message
*/ staticvoid timerlat_top_usage(char *usage)
{ int i;
staticconstchar *const msg[] = { "", " usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", " [[-t[file]] [-e sys[:event]] [--filter ] [--trigger ] [-c cpu-list] [-H cpu-list]\\", " [-P priority] [--dma-latency us] [--aa-only us] [-C[=cgroup_name]] [-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", " --aa-only us: stop if latency is hit, only printing the auto analysis (reduces CPU usage)", " -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[s|m|h|d]: duration of the session", " -D/--debug: print debug info", " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)", " -t/--trace[file]: save the stopped trace to [file|timerlat_trace.txt]", " -e/--event : enable the in the trace instance, multiple -e are allowed", " --filter : enable a trace event filter to the previous -e event", " --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", " -q/--quiet print only a summary at the end", " --dma-latency us: set /dev/cpu_dma_latency latency 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 : define action to be executed at latency threshold, multiple are allowed", " --on-end: define action to be executed at measurement end, multiple are allowed",
NULL,
};
if (usage)
fprintf(stderr, "%s\n", usage);
fprintf(stderr, "rtla timerlat top: a per-cpu summary 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_top_parse_args - allocs, parse and fill the cmd line parameters
*/ staticstruct timerlat_params
*timerlat_top_parse_args(int argc, char **argv)
{ struct timerlat_params *params; struct trace_events *tevent; longlong auto_thresh; int retval; int c; char *trace_output = NULL;
params = calloc(1, sizeof(*params)); if (!params) exit(1);
if (trace_output)
actions_add_trace_output(¶ms->threshold_actions, trace_output);
if (geteuid()) {
err_msg("rtla needs root permission\n"); exit(EXIT_FAILURE);
}
/* * Auto analysis only happens if stop tracing, thus:
*/ if (!params->stop_us && !params->stop_total_us)
params->no_aa = 1;
if (params->no_aa && params->aa_only)
timerlat_top_usage("--no-aa and --aa-only are mutually exclusive!");
if (params->kernel_workload && params->user_workload)
timerlat_top_usage("--kernel-threads and --user-threads are mutually exclusive!");
/* * If auto-analysis or trace output is enabled, switch from BPF mode to * 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->mode = TRACING_MODE_MIXED;
return params;
}
/* * timerlat_top_apply_config - apply the top configs to the initialized tool
*/ staticint
timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_params *params)
{ int retval;
retval = timerlat_apply_config(top, params); if (retval) goto out_err;
if (isatty(STDOUT_FILENO) && !params->quiet)
params->pretty_output = 1;
return 0;
out_err: return -1;
}
/* * timerlat_init_top - initialize a timerlat top tool with parameters
*/ staticstruct osnoise_tool
*timerlat_init_top(struct timerlat_params *params)
{ struct osnoise_tool *top; int nr_cpus;
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
top = osnoise_init_tool("timerlat_top"); if (!top) return NULL;
top->data = timerlat_alloc_top(nr_cpus); if (!top->data) goto out_err;
staticint stop_tracing; staticstruct trace_instance *top_inst = NULL; staticvoid 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
*/ staticvoid
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
*/ staticint
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)
*/ staticint
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)
{ int retval, wait_retval;
if (params->aa_only) { /* Auto-analysis only, just wait for stop tracing */
timerlat_bpf_wait(-1); return 0;
}
/* Pull and display data in a loop */ while (!stop_tracing) {
wait_retval = timerlat_bpf_wait(params->quiet ? -1 : params->sleep_time);
if (!params->quiet)
timerlat_print_stats(params, top);
if (wait_retval == 1) { /* Stopping requested by tracer */
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();
}
/* 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;
}
int timerlat_top_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 *top = NULL; struct osnoise_tool *aa = NULL; struct trace_instance *trace; int dma_latency_fd = -1;
pthread_t timerlat_u; char *max_lat; 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;
} 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_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;
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);
} else {
retval = timerlat_bpf_attach(); if (retval) {
err_msg("Error attaching BPF program\n"); goto out_top;
}
}
if (params->mode != TRACING_MODE_TRACEFS)
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;
} elseif (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);
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.35 Sekunden
(vorverarbeitet)
¤
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.