// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> */#define _GNU_SOURCE#include <sys/types.h>#include <sys/stat.h>#include <pthread.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <stdio.h>#include <sched.h> #include <linux/compiler.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 */inttimerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params){ int retval; const char *const rtla_no_bpf = getenv("RTLA_NO_BPF"); /* * Try to enable BPF, unless disabled explicitly. * If BPF enablement fails, fall back to tracefs mode. */ if (rtla_no_bpf && strncmp_static(rtla_no_bpf, "1") == 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; } } /* Check if BPF action program is requested but BPF is not available */ if (params->bpf_action_program) { if (params->mode == TRACING_MODE_TRACEFS) { err_msg("BPF actions are not supported in tracefs-only mode\n"); goto out_err; } if (timerlat_load_bpf_action_program(params->bpf_action_program)) goto out_err; } retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us ? params->timerlat_period_us : DEFAULT_TIMERLAT_PERIOD); if (retval) { err_msg("Failed to set timerlat period\n"); goto out_err; } retval = osnoise_set_print_stack(tool->context, params->print_stack); if (retval) { err_msg("Failed to set print stack\n"); goto out_err; } retval = osnoise_set_timerlat_align(tool->context, params->timerlat_align); if (retval && params->timerlat_align) { /* * We might be running on a kernel that does not support timerlat align. * Unless user requested it explicitly, ignore the error. */ err_msg("Failed to enable timerlat align\n"); goto out_err; } if (params->timerlat_align) { retval = osnoise_set_timerlat_align_us(tool->context, params->timerlat_align_us); if (retval) { err_msg("Failed to set timerlat align us\n"); goto out_err; } } /* * If the user did not specify a type of thread, try user-threads first. * Fall back to kernel threads otherwise. */ 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->common.user_workload = 1; params->common.user_data = 1; } else { debug_msg("User-space interface not detected, setting kernel-threads\n"); params->common.kernel_workload = 1; } } 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, 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; } for_each_monitored_cpu(i, ¶ms->common) { 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, params->stack_format); 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; } } /* * In tracefs and mixed mode, timerlat tracer handles stopping * on threshold */ if (params->mode != TRACING_MODE_BPF) { retval = osn_set_stop(tool); if (retval) 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(tool->trace.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 i; timerlat_aa_destroy(); if (dma_latency_fd >= 0) close(dma_latency_fd); if (params->deepest_idle_state >= -1) { for_each_monitored_cpu(i, ¶ms->common) { 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();} __noreturn static void timerlat_usage(int err){ int i; static const char * const msg[] = { "", "timerlat version " VERSION, "", " usage: [rtla] timerlat [MODE] ...", "", " modes:", " top - prints the summary from timerlat tracer", " hist - prints a histogram of timer latencies", "", "if no MODE is given, the top mode is called, passing the arguments", NULL, }; for (i = 0; msg[i]; i++) fprintf(stderr, "%s\n", msg[i]); exit(err);} int timerlat_main(int argc, char *argv[]){ if (argc == 0) goto usage; /* * if timerlat was called without any argument, run the * default cmdline. */ if (argc == 1) { run_tool(&timerlat_top_ops, argc, argv); exit(0); } if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { timerlat_usage(129); } else if (str_has_prefix(argv[1], "-")) { /* the user skipped the tool, call the default one */ run_tool(&timerlat_top_ops, argc, argv); exit(0); } else if (strcmp(argv[1], "top") == 0) { run_tool(&timerlat_top_ops, argc-1, &argv[1]); exit(0); } else if (strcmp(argv[1], "hist") == 0) { run_tool(&timerlat_hist_ops, argc-1, &argv[1]); exit(0); } usage: timerlat_usage(129);}
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sched.h>
#include <linux/compiler.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;
const char *const rtla_no_bpf = getenv("RTLA_NO_BPF");
/*
* Try to enable BPF, unless disabled explicitly.
* If BPF enablement fails, fall back to tracefs mode.
*/
if (rtla_no_bpf && strncmp_static(rtla_no_bpf, "1") == 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;
}
}
/* Check if BPF action program is requested but BPF is not available */
if (params->bpf_action_program) {
if (params->mode == TRACING_MODE_TRACEFS) {
err_msg("BPF actions are not supported in tracefs-only mode\n");
goto out_err;
}
if (timerlat_load_bpf_action_program(params->bpf_action_program))
goto out_err;
}
retval = osnoise_set_timerlat_period_us(tool->context,
params->timerlat_period_us ?
params->timerlat_period_us :
DEFAULT_TIMERLAT_PERIOD);
if (retval) {
err_msg("Failed to set timerlat period\n");
goto out_err;
}
retval = osnoise_set_print_stack(tool->context, params->print_stack);
if (retval) {
err_msg("Failed to set print stack\n");
goto out_err;
}
retval = osnoise_set_timerlat_align(tool->context, params->timerlat_align);
if (retval && params->timerlat_align) {
/*
* We might be running on a kernel that does not support timerlat align.
* Unless user requested it explicitly, ignore the error.
*/
err_msg("Failed to enable timerlat align\n");
goto out_err;
}
if (params->timerlat_align) {
retval = osnoise_set_timerlat_align_us(tool->context, params->timerlat_align_us);
if (retval) {
err_msg("Failed to set timerlat align us\n");
goto out_err;
}
}
/*
* If the user did not specify a type of thread, try user-threads first.
* Fall back to kernel threads otherwise.
*/
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->common.user_workload = 1;
params->common.user_data = 1;
} else {
debug_msg("User-space interface not detected, setting kernel-threads\n");
params->common.kernel_workload = 1;
}
}
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, 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;
}
for_each_monitored_cpu(i, ¶ms->common) {
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, params->stack_format);
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;
}
}
/*
* In tracefs and mixed mode, timerlat tracer handles stopping
* on threshold
*/
if (params->mode != TRACING_MODE_BPF) {
retval = osn_set_stop(tool);
if (retval)
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(tool->trace.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 i;
timerlat_aa_destroy();
if (dma_latency_fd >= 0)
close(dma_latency_fd);
if (params->deepest_idle_state >= -1) {
for_each_monitored_cpu(i, ¶ms->common) {
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();
}
__noreturn static void timerlat_usage(int err)
{
int i;
static const char * const msg[] = {
"",
"timerlat version " VERSION,
"",
" usage: [rtla] timerlat [MODE] ...",
"",
" modes:",
" top - prints the summary from timerlat tracer",
" hist - prints a histogram of timer latencies",
"",
"if no MODE is given, the top mode is called, passing the arguments",
NULL,
};
for (i = 0; msg[i]; i++)
fprintf(stderr, "%s\n", msg[i]);
exit(err);
}
int timerlat_main(int argc, char *argv[])
{
if (argc == 0)
goto usage;
/*
* if timerlat was called without any argument, run the
* default cmdline.
*/
if (argc == 1) {
run_tool(&timerlat_top_ops, argc, argv);
exit(0);
}
if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
timerlat_usage(129);
} else if (str_has_prefix(argv[1], "-")) {
/* the user skipped the tool, call the default one */
run_tool(&timerlat_top_ops, argc, argv);
exit(0);
} else if (strcmp(argv[1], "top") == 0) {
run_tool(&timerlat_top_ops, argc-1, &argv[1]);
exit(0);
} else if (strcmp(argv[1], "hist") == 0) {
run_tool(&timerlat_hist_ops, argc-1, &argv[1]);
exit(0);
}
usage:
timerlat_usage(129);
}