summaryrefslogtreecommitdiff
path: root/tools/tracing
diff options
context:
space:
mode:
Diffstat (limited to 'tools/tracing')
-rw-r--r--tools/tracing/latency/Makefile6
-rw-r--r--tools/tracing/rtla/.gitignore1
-rw-r--r--tools/tracing/rtla/Makefile30
-rw-r--r--tools/tracing/rtla/Makefile.config42
-rw-r--r--tools/tracing/rtla/Makefile.rtla17
-rw-r--r--tools/tracing/rtla/README.txt7
-rw-r--r--tools/tracing/rtla/src/Build1
-rw-r--r--tools/tracing/rtla/src/osnoise.c124
-rw-r--r--tools/tracing/rtla/src/osnoise.h52
-rw-r--r--tools/tracing/rtla/src/osnoise_hist.c134
-rw-r--r--tools/tracing/rtla/src/osnoise_top.c136
-rw-r--r--tools/tracing/rtla/src/timerlat.bpf.c149
-rw-r--r--tools/tracing/rtla/src/timerlat.c106
-rw-r--r--tools/tracing/rtla/src/timerlat.h54
-rw-r--r--tools/tracing/rtla/src/timerlat_aa.c2
-rw-r--r--tools/tracing/rtla/src/timerlat_bpf.c167
-rw-r--r--tools/tracing/rtla/src/timerlat_bpf.h59
-rw-r--r--tools/tracing/rtla/src/timerlat_hist.c378
-rw-r--r--tools/tracing/rtla/src/timerlat_top.c507
-rw-r--r--tools/tracing/rtla/src/trace.c69
-rw-r--r--tools/tracing/rtla/src/trace.h4
-rw-r--r--tools/tracing/rtla/src/utils.c2
-rw-r--r--tools/tracing/rtla/src/utils.h6
-rw-r--r--tools/tracing/rtla/tests/engine.sh117
-rw-r--r--tools/tracing/rtla/tests/hwnoise.t21
-rw-r--r--tools/tracing/rtla/tests/osnoise.t25
-rw-r--r--tools/tracing/rtla/tests/timerlat.t41
27 files changed, 1590 insertions, 667 deletions
diff --git a/tools/tracing/latency/Makefile b/tools/tracing/latency/Makefile
index 6518b03e05c7..257a56b1899f 100644
--- a/tools/tracing/latency/Makefile
+++ b/tools/tracing/latency/Makefile
@@ -37,12 +37,6 @@ FEATURE_TESTS += libtracefs
FEATURE_DISPLAY := libtraceevent
FEATURE_DISPLAY += libtracefs
-ifeq ($(V),1)
- Q =
-else
- Q = @
-endif
-
all: $(LATENCY-COLLECTOR)
include $(srctree)/tools/build/Makefile.include
diff --git a/tools/tracing/rtla/.gitignore b/tools/tracing/rtla/.gitignore
index 293f0dbb0ca2..1a394ad26cc1 100644
--- a/tools/tracing/rtla/.gitignore
+++ b/tools/tracing/rtla/.gitignore
@@ -4,3 +4,4 @@ rtla-static
fixdep
feature
FEATURE-DUMP
+*.skel.h
diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile
index a6a7dee16622..746ccf2f5808 100644
--- a/tools/tracing/rtla/Makefile
+++ b/tools/tracing/rtla/Makefile
@@ -33,15 +33,15 @@ DOCSRC := ../../../Documentation/tools/rtla/
FEATURE_TESTS := libtraceevent
FEATURE_TESTS += libtracefs
FEATURE_TESTS += libcpupower
+FEATURE_TESTS += libbpf
+FEATURE_TESTS += clang-bpf-co-re
+FEATURE_TESTS += bpftool-skeletons
FEATURE_DISPLAY := libtraceevent
FEATURE_DISPLAY += libtracefs
FEATURE_DISPLAY += libcpupower
-
-ifeq ($(V),1)
- Q =
-else
- Q = @
-endif
+FEATURE_DISPLAY += libbpf
+FEATURE_DISPLAY += clang-bpf-co-re
+FEATURE_DISPLAY += bpftool-skeletons
all: $(RTLA)
@@ -67,6 +67,17 @@ CFLAGS += $(INCLUDES) $(LIB_INCLUDES)
export CFLAGS OUTPUT srctree
+ifeq ($(BUILD_BPF_SKEL),1)
+src/timerlat.bpf.o: src/timerlat.bpf.c
+ $(QUIET_CLANG)$(CLANG) -g -O2 -target bpf -c $(filter %.c,$^) -o $@
+
+src/timerlat.skel.h: src/timerlat.bpf.o
+ $(QUIET_GENSKEL)$(SYSTEM_BPFTOOL) gen skeleton $< > $@
+else
+src/timerlat.skel.h:
+ $(Q)echo '/* BPF skeleton is disabled */' > src/timerlat.skel.h
+endif
+
$(RTLA): $(RTLA_IN)
$(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(EXTLIBS)
@@ -77,7 +88,7 @@ static: $(RTLA_IN)
rtla.%: fixdep FORCE
make -f $(srctree)/tools/build/Makefile.build dir=. $@
-$(RTLA_IN): fixdep FORCE
+$(RTLA_IN): fixdep FORCE src/timerlat.skel.h
make $(build)=rtla
clean: doc_clean fixdep-clean
@@ -85,4 +96,7 @@ clean: doc_clean fixdep-clean
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)rm -f rtla rtla-static fixdep FEATURE-DUMP rtla-*
$(Q)rm -rf feature
-.PHONY: FORCE clean
+ $(Q)rm -f src/timerlat.bpf.o src/timerlat.skel.h
+check: $(RTLA)
+ RTLA=$(RTLA) prove -o -f tests/
+.PHONY: FORCE clean check
diff --git a/tools/tracing/rtla/Makefile.config b/tools/tracing/rtla/Makefile.config
index 92a6e12e42d3..5f2231d8d626 100644
--- a/tools/tracing/rtla/Makefile.config
+++ b/tools/tracing/rtla/Makefile.config
@@ -53,6 +53,48 @@ else
$(info Please install libcpupower-dev/kernel-tools-libs-devel)
endif
+ifndef BUILD_BPF_SKEL
+ # BPF skeletons are used to implement improved sample collection, enable
+ # them by default.
+ BUILD_BPF_SKEL := 1
+endif
+
+ifeq ($(BUILD_BPF_SKEL),0)
+ $(info BPF skeleton support disabled, building without BPF skeleton support.)
+endif
+
+$(call feature_check,libbpf)
+ifeq ($(feature-libbpf), 1)
+ $(call detected,CONFIG_LIBBPF)
+else
+ $(info libbpf is missing, building without BPF skeleton support.)
+ $(info Please install libbpf-dev/libbpf-devel)
+ BUILD_BPF_SKEL := 0
+endif
+
+$(call feature_check,clang-bpf-co-re)
+ifeq ($(feature-clang-bpf-co-re), 1)
+ $(call detected,CONFIG_CLANG_BPF_CO_RE)
+else
+ $(info clang is missing or does not support BPF CO-RE, building without BPF skeleton support.)
+ $(info Please install clang)
+ BUILD_BPF_SKEL := 0
+endif
+
+$(call feature_check,bpftool-skeletons)
+ifeq ($(feature-bpftool-skeletons), 1)
+ $(call detected,CONFIG_BPFTOOL_SKELETONS)
+else
+ $(info bpftool is missing or not supporting skeletons, building without BPF skeleton support.)
+ $(info Please install bpftool)
+ BUILD_BPF_SKEL := 0
+endif
+
+ifeq ($(BUILD_BPF_SKEL),1)
+ CFLAGS += -DHAVE_BPF_SKEL
+ EXTLIBS += -lbpf
+endif
+
ifeq ($(STOP_ERROR),1)
$(error Please, check the errors above.)
endif
diff --git a/tools/tracing/rtla/Makefile.rtla b/tools/tracing/rtla/Makefile.rtla
index cc1d6b615475..08c1b40883d3 100644
--- a/tools/tracing/rtla/Makefile.rtla
+++ b/tools/tracing/rtla/Makefile.rtla
@@ -34,6 +34,8 @@ INSTALL := install
MKDIR := mkdir
STRIP := strip
BINDIR := /usr/bin
+CTAGS := ctags
+ETAGS := ctags -e
.PHONY: install
install: doc_install
@@ -47,6 +49,18 @@ install: doc_install
@test ! -f $(DESTDIR)$(BINDIR)/timerlat || $(RM) $(DESTDIR)$(BINDIR)/timerlat
@$(LN) -s rtla $(DESTDIR)$(BINDIR)/timerlat
+.PHONY: tags
+tags:
+ $(CTAGS) -R --extras=+f --c-kinds=+p src
+
+.PHONY: TAGS
+TAGS:
+ $(ETAGS) -R --extras=+f --c-kinds=+p src
+
+.PHONY: tags_clean
+tags_clean:
+ $(RM) tags TAGS
+
.PHONY: doc doc_clean doc_install
doc:
$(MAKE) -C $(DOCSRC)
@@ -57,8 +71,7 @@ doc_clean:
doc_install:
$(MAKE) -C $(DOCSRC) install
-# This section is neesary for the tarball, when the tarball
-# support is removed, we can delete these entries.
+# This section is necessary to make the rtla tarball
NAME := rtla
DIRS := src
FILES := Makefile README.txt
diff --git a/tools/tracing/rtla/README.txt b/tools/tracing/rtla/README.txt
index dd5621038c55..43e98311d10f 100644
--- a/tools/tracing/rtla/README.txt
+++ b/tools/tracing/rtla/README.txt
@@ -13,6 +13,13 @@ RTLA depends on the following libraries and tools:
- libtraceevent
- libcpupower (optional, for --deepest-idle-state)
+For BPF sample collection support, the following extra dependencies are
+required:
+
+ - libbpf 1.0.0 or later
+ - bpftool with skeleton support
+ - clang with BPF CO-RE support
+
It also depends on python3-docutils to compile man pages.
For development, we suggest the following steps for compiling rtla:
diff --git a/tools/tracing/rtla/src/Build b/tools/tracing/rtla/src/Build
index dbed9e31829b..7bb7e39e391a 100644
--- a/tools/tracing/rtla/src/Build
+++ b/tools/tracing/rtla/src/Build
@@ -8,4 +8,5 @@ rtla-y += timerlat_top.o
rtla-y += timerlat_hist.o
rtla-y += timerlat_u.o
rtla-y += timerlat_aa.o
+rtla-y += timerlat_bpf.o
rtla-y += rtla.o
diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c
index 245e9344932b..2dc3e4539e99 100644
--- a/tools/tracing/rtla/src/osnoise.c
+++ b/tools/tracing/rtla/src/osnoise.c
@@ -3,6 +3,7 @@
* 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>
@@ -12,9 +13,12 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
+#include <sched.h>
#include "osnoise.h"
-#include "utils.h"
+
+#define DEFAULT_SAMPLE_PERIOD 1000000 /* 1s */
+#define DEFAULT_SAMPLE_RUNTIME 1000000 /* 1s */
/*
* osnoise_get_cpus - return the original "osnoise/cpus" content
@@ -867,7 +871,7 @@ int osnoise_set_workload(struct osnoise_context *context, bool onoff)
retval = osnoise_options_set_option("OSNOISE_WORKLOAD", onoff);
if (retval < 0)
- return -1;
+ return -2;
context->opt_workload = onoff;
@@ -1079,6 +1083,122 @@ out_err:
return NULL;
}
+bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record)
+{
+ /*
+ * The tool instance is always present, it is the one used to collect
+ * data.
+ */
+ if (!tracefs_trace_is_on(tool->trace.inst))
+ return true;
+
+ /*
+ * The trace record instance is only enabled when -t is set. IOW, when the system
+ * is tracing.
+ */
+ return record && !tracefs_trace_is_on(record->trace.inst);
+}
+
+/*
+ * osnoise_report_missed_events - report number of events dropped by trace
+ * buffer
+ */
+void
+osnoise_report_missed_events(struct osnoise_tool *tool)
+{
+ unsigned long long total_events;
+
+ if (tool->trace.missed_events == UINT64_MAX)
+ printf("unknown number of events missed, results might not be accurate\n");
+ else if (tool->trace.missed_events > 0) {
+ total_events = tool->trace.processed_events + tool->trace.missed_events;
+
+ printf("%lld (%.2f%%) events missed, results might not be accurate\n",
+ tool->trace.missed_events,
+ (double) tool->trace.missed_events / total_events * 100.0);
+ }
+}
+
+/*
+ * osnoise_apply_config - apply common 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;
+ }
+
+ if (params->runtime || params->period) {
+ retval = osnoise_set_runtime_period(tool->context,
+ params->runtime,
+ params->period);
+ } else {
+ retval = osnoise_set_runtime_period(tool->context,
+ DEFAULT_SAMPLE_PERIOD,
+ DEFAULT_SAMPLE_RUNTIME);
+ }
+
+ if (retval) {
+ err_msg("Failed to set runtime and/or period\n");
+ goto out_err;
+ }
+
+ retval = osnoise_set_stop_us(tool->context, params->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);
+ if (retval) {
+ err_msg("Failed to set stop total us\n");
+ goto out_err;
+ }
+
+ retval = osnoise_set_tracing_thresh(tool->context, params->threshold);
+ if (retval) {
+ err_msg("Failed to set tracing_thresh\n");
+ goto out_err;
+ }
+
+ if (params->hk_cpus) {
+ retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
+ &params->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(&params->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;
+
+out_err:
+ return -1;
+}
+
static void osnoise_usage(int err)
{
int i;
diff --git a/tools/tracing/rtla/src/osnoise.h b/tools/tracing/rtla/src/osnoise.h
index 555f4f4903cc..ac1c99910744 100644
--- a/tools/tracing/rtla/src/osnoise.h
+++ b/tools/tracing/rtla/src/osnoise.h
@@ -1,6 +1,55 @@
// SPDX-License-Identifier: GPL-2.0
+#pragma once
+
+#include "utils.h"
#include "trace.h"
+enum osnoise_mode {
+ MODE_OSNOISE = 0,
+ MODE_HWNOISE
+};
+
+struct osnoise_params {
+ /* Common params */
+ char *cpus;
+ cpu_set_t monitored_cpus;
+ char *trace_output;
+ char *cgroup_name;
+ 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;
+ };
+ };
+};
+
/*
* osnoise_context - read, store, write, restore osnoise configs.
*/
@@ -104,6 +153,9 @@ struct osnoise_tool {
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);
diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c
index 214e2c93fde0..8d579bcee709 100644
--- a/tools/tracing/rtla/src/osnoise_hist.c
+++ b/tools/tracing/rtla/src/osnoise_hist.c
@@ -12,40 +12,9 @@
#include <errno.h>
#include <stdio.h>
#include <time.h>
-#include <sched.h>
-#include "utils.h"
#include "osnoise.h"
-struct osnoise_hist_params {
- char *cpus;
- cpu_set_t monitored_cpus;
- char *trace_output;
- char *cgroup_name;
- 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 output_divisor;
- int cgroup;
- int hk_cpus;
- cpu_set_t hk_cpu_set;
- struct sched_attr sched_param;
- struct trace_events *events;
- char no_header;
- char no_summary;
- char no_index;
- char with_zeros;
- int bucket_size;
- int entries;
- int warmup;
- int buffer_size;
-};
-
struct osnoise_hist_cpu {
int *samples;
int count;
@@ -126,7 +95,7 @@ cleanup:
static void osnoise_hist_update_multiple(struct osnoise_tool *tool, int cpu,
unsigned long long duration, int count)
{
- struct osnoise_hist_params *params = tool->params;
+ struct osnoise_params *params = tool->params;
struct osnoise_hist_data *data = tool->data;
unsigned long long total_duration;
int entries = data->entries;
@@ -168,7 +137,7 @@ static void osnoise_destroy_trace_hist(struct osnoise_tool *tool)
*/
static int osnoise_init_trace_hist(struct osnoise_tool *tool)
{
- struct osnoise_hist_params *params = tool->params;
+ struct osnoise_params *params = tool->params;
struct osnoise_hist_data *data = tool->data;
int bucket_size;
char buff[128];
@@ -253,7 +222,7 @@ static void osnoise_read_trace_hist(struct osnoise_tool *tool)
*/
static void osnoise_hist_header(struct osnoise_tool *tool)
{
- struct osnoise_hist_params *params = tool->params;
+ struct osnoise_params *params = tool->params;
struct osnoise_hist_data *data = tool->data;
struct trace_seq *s = tool->trace.seq;
char duration[26];
@@ -292,7 +261,7 @@ static void osnoise_hist_header(struct osnoise_tool *tool)
* osnoise_print_summary - print the summary of the hist data to the output
*/
static void
-osnoise_print_summary(struct osnoise_hist_params *params,
+osnoise_print_summary(struct osnoise_params *params,
struct trace_instance *trace,
struct osnoise_hist_data *data)
{
@@ -370,7 +339,7 @@ osnoise_print_summary(struct osnoise_hist_params *params,
* osnoise_print_stats - print data for all CPUs
*/
static void
-osnoise_print_stats(struct osnoise_hist_params *params, struct osnoise_tool *tool)
+osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool)
{
struct osnoise_hist_data *data = tool->data;
struct trace_instance *trace = &tool->trace;
@@ -440,6 +409,7 @@ osnoise_print_stats(struct osnoise_hist_params *params, struct osnoise_tool *too
trace_seq_reset(trace->seq);
osnoise_print_summary(params, trace, data);
+ osnoise_report_missed_events(tool);
}
/*
@@ -507,10 +477,10 @@ static void osnoise_hist_usage(char *usage)
/*
* osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters
*/
-static struct osnoise_hist_params
+static struct osnoise_params
*osnoise_hist_parse_args(int argc, char *argv[])
{
- struct osnoise_hist_params *params;
+ struct osnoise_params *params;
struct trace_events *tevent;
int retval;
int c;
@@ -730,72 +700,13 @@ static struct osnoise_hist_params
* osnoise_hist_apply_config - apply the hist configs to the initialized tool
*/
static int
-osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_hist_params *params)
+osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)
{
int retval;
- if (!params->sleep_time)
- params->sleep_time = 1;
-
- if (params->cpus) {
- retval = osnoise_set_cpus(tool->context, params->cpus);
- if (retval) {
- err_msg("Failed to apply CPUs config\n");
- goto out_err;
- }
- }
-
- if (params->runtime || params->period) {
- retval = osnoise_set_runtime_period(tool->context,
- params->runtime,
- params->period);
- if (retval) {
- err_msg("Failed to set runtime and/or period\n");
- goto out_err;
- }
- }
-
- if (params->stop_us) {
- retval = osnoise_set_stop_us(tool->context, params->stop_us);
- if (retval) {
- err_msg("Failed to set stop us\n");
- goto out_err;
- }
- }
-
- if (params->stop_total_us) {
- retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
- if (retval) {
- err_msg("Failed to set stop total us\n");
- goto out_err;
- }
- }
-
- if (params->threshold) {
- retval = osnoise_set_tracing_thresh(tool->context, params->threshold);
- if (retval) {
- err_msg("Failed to set tracing_thresh\n");
- goto out_err;
- }
- }
-
- if (params->hk_cpus) {
- retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
- &params->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(&params->monitored_cpus);
- }
+ retval = osnoise_apply_config(tool, params);
+ if (retval)
+ goto out_err;
return 0;
@@ -807,7 +718,7 @@ out_err:
* osnoise_init_hist - initialize a osnoise hist tool with parameters
*/
static struct osnoise_tool
-*osnoise_init_hist(struct osnoise_hist_params *params)
+*osnoise_init_hist(struct osnoise_params *params)
{
struct osnoise_tool *tool;
int nr_cpus;
@@ -841,7 +752,7 @@ static void stop_hist(int sig)
* osnoise_hist_set_signals - handles the signal to stop the tool
*/
static void
-osnoise_hist_set_signals(struct osnoise_hist_params *params)
+osnoise_hist_set_signals(struct osnoise_params *params)
{
signal(SIGINT, stop_hist);
if (params->duration) {
@@ -852,11 +763,11 @@ osnoise_hist_set_signals(struct osnoise_hist_params *params)
int osnoise_hist_main(int argc, char *argv[])
{
- struct osnoise_hist_params *params;
+ struct osnoise_params *params;
struct osnoise_tool *record = NULL;
struct osnoise_tool *tool = NULL;
+ enum result return_value = ERROR;
struct trace_instance *trace;
- int return_value = 1;
int retval;
params = osnoise_hist_parse_args(argc, argv);
@@ -970,7 +881,7 @@ int osnoise_hist_main(int argc, char *argv[])
goto out_hist;
}
- if (trace_is_off(&tool->trace, &record->trace))
+ if (osnoise_trace_is_off(tool, record))
break;
}
@@ -978,14 +889,13 @@ int osnoise_hist_main(int argc, char *argv[])
osnoise_print_stats(params, tool);
- return_value = 0;
+ return_value = PASSED;
- if (trace_is_off(&tool->trace, &record->trace)) {
+ if (osnoise_trace_is_off(tool, record)) {
printf("rtla osnoise hit stop tracing\n");
- if (params->trace_output) {
- printf(" Saving trace to %s\n", params->trace_output);
- save_trace_to_file(record->trace.inst, params->trace_output);
- }
+ save_trace_to_file(record ? record->trace.inst : NULL,
+ params->trace_output);
+ return_value = FAILED;
}
out_hist:
diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c
index 45647495ce3b..2c12780c8aa9 100644
--- a/tools/tracing/rtla/src/osnoise_top.c
+++ b/tools/tracing/rtla/src/osnoise_top.c
@@ -11,43 +11,8 @@
#include <unistd.h>
#include <stdio.h>
#include <time.h>
-#include <sched.h>
#include "osnoise.h"
-#include "utils.h"
-
-enum osnoise_mode {
- MODE_OSNOISE = 0,
- MODE_HWNOISE
-};
-
-/*
- * osnoise top parameters
- */
-struct osnoise_top_params {
- char *cpus;
- cpu_set_t monitored_cpus;
- char *trace_output;
- char *cgroup_name;
- 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 quiet;
- int set_sched;
- int cgroup;
- int hk_cpus;
- int warmup;
- int buffer_size;
- int pretty_output;
- cpu_set_t hk_cpu_set;
- struct sched_attr sched_param;
- struct trace_events *events;
- enum osnoise_mode mode;
-};
struct osnoise_top_cpu {
unsigned long long sum_runtime;
@@ -158,7 +123,7 @@ osnoise_top_handler(struct trace_seq *s, struct tep_record *record,
*/
static void osnoise_top_header(struct osnoise_tool *top)
{
- struct osnoise_top_params *params = top->params;
+ struct osnoise_params *params = top->params;
struct trace_seq *s = top->trace.seq;
char duration[26];
@@ -218,7 +183,7 @@ static void clear_terminal(struct trace_seq *seq)
*/
static void osnoise_top_print(struct osnoise_tool *tool, int cpu)
{
- struct osnoise_top_params *params = tool->params;
+ struct osnoise_params *params = tool->params;
struct trace_seq *s = tool->trace.seq;
struct osnoise_top_cpu *cpu_data;
struct osnoise_top_data *data;
@@ -258,7 +223,7 @@ 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_top_params *params, struct osnoise_tool *top)
+osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *top)
{
struct trace_instance *trace = &top->trace;
static int nr_cpus = -1;
@@ -280,12 +245,13 @@ osnoise_print_stats(struct osnoise_top_params *params, struct osnoise_tool *top)
trace_seq_do_printf(trace->seq);
trace_seq_reset(trace->seq);
+ osnoise_report_missed_events(top);
}
/*
* osnoise_top_usage - prints osnoise top usage message
*/
-static void osnoise_top_usage(struct osnoise_top_params *params, char *usage)
+static void osnoise_top_usage(struct osnoise_params *params, char *usage)
{
int i;
@@ -353,9 +319,9 @@ static void osnoise_top_usage(struct osnoise_top_params *params, char *usage)
/*
* osnoise_top_parse_args - allocs, parse and fill the cmd line parameters
*/
-struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
+struct osnoise_params *osnoise_top_parse_args(int argc, char **argv)
{
- struct osnoise_top_params *params;
+ struct osnoise_params *params;
struct trace_events *tevent;
int retval;
int c;
@@ -552,54 +518,13 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
* osnoise_top_apply_config - apply the top configs to the initialized tool
*/
static int
-osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_top_params *params)
+osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)
{
int retval;
- if (!params->sleep_time)
- params->sleep_time = 1;
-
- if (params->cpus) {
- retval = osnoise_set_cpus(tool->context, params->cpus);
- if (retval) {
- err_msg("Failed to apply CPUs config\n");
- goto out_err;
- }
- }
-
- if (params->runtime || params->period) {
- retval = osnoise_set_runtime_period(tool->context,
- params->runtime,
- params->period);
- if (retval) {
- err_msg("Failed to set runtime and/or period\n");
- goto out_err;
- }
- }
-
- if (params->stop_us) {
- retval = osnoise_set_stop_us(tool->context, params->stop_us);
- if (retval) {
- err_msg("Failed to set stop us\n");
- goto out_err;
- }
- }
-
- if (params->stop_total_us) {
- retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
- if (retval) {
- err_msg("Failed to set stop total us\n");
- goto out_err;
- }
- }
-
- if (params->threshold) {
- retval = osnoise_set_tracing_thresh(tool->context, params->threshold);
- if (retval) {
- err_msg("Failed to set tracing_thresh\n");
- goto out_err;
- }
- }
+ retval = osnoise_apply_config(tool, params);
+ if (retval)
+ goto out_err;
if (params->mode == MODE_HWNOISE) {
retval = osnoise_set_irq_disable(tool->context, 1);
@@ -609,24 +534,6 @@ osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_top_params *p
}
}
- if (params->hk_cpus) {
- retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
- &params->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(&params->monitored_cpus);
- }
-
if (isatty(STDOUT_FILENO) && !params->quiet)
params->pretty_output = 1;
@@ -639,7 +546,7 @@ out_err:
/*
* osnoise_init_top - initialize a osnoise top tool with parameters
*/
-struct osnoise_tool *osnoise_init_top(struct osnoise_top_params *params)
+struct osnoise_tool *osnoise_init_top(struct osnoise_params *params)
{
struct osnoise_tool *tool;
int nr_cpus;
@@ -673,7 +580,7 @@ static void stop_top(int sig)
/*
* osnoise_top_set_signals - handles the signal to stop the tool
*/
-static void osnoise_top_set_signals(struct osnoise_top_params *params)
+static void osnoise_top_set_signals(struct osnoise_params *params)
{
signal(SIGINT, stop_top);
if (params->duration) {
@@ -684,11 +591,11 @@ static void osnoise_top_set_signals(struct osnoise_top_params *params)
int osnoise_top_main(int argc, char **argv)
{
- struct osnoise_top_params *params;
+ struct osnoise_params *params;
struct osnoise_tool *record = NULL;
struct osnoise_tool *tool = NULL;
+ enum result return_value = ERROR;
struct trace_instance *trace;
- int return_value = 1;
int retval;
params = osnoise_top_parse_args(argc, argv);
@@ -801,21 +708,20 @@ int osnoise_top_main(int argc, char **argv)
if (!params->quiet)
osnoise_print_stats(params, tool);
- if (trace_is_off(&tool->trace, &record->trace))
+ if (osnoise_trace_is_off(tool, record))
break;
}
osnoise_print_stats(params, tool);
- return_value = 0;
+ return_value = PASSED;
- if (trace_is_off(&tool->trace, &record->trace)) {
+ if (osnoise_trace_is_off(tool, record)) {
printf("osnoise hit stop tracing\n");
- if (params->trace_output) {
- printf(" Saving trace to %s\n", params->trace_output);
- save_trace_to_file(record->trace.inst, params->trace_output);
- }
+ save_trace_to_file(record ? record->trace.inst : NULL,
+ params->trace_output);
+ return_value = FAILED;
}
out_top:
diff --git a/tools/tracing/rtla/src/timerlat.bpf.c b/tools/tracing/rtla/src/timerlat.bpf.c
new file mode 100644
index 000000000000..96196d46e170
--- /dev/null
+++ b/tools/tracing/rtla/src/timerlat.bpf.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_tracing.h>
+#include <stdbool.h>
+#include "timerlat_bpf.h"
+
+#define nosubprog __always_inline
+#define MAX_ENTRIES_DEFAULT 4096
+
+char LICENSE[] SEC("license") = "GPL";
+
+struct trace_event_raw_timerlat_sample {
+ unsigned long long timer_latency;
+ int context;
+} __attribute__((preserve_access_index));
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, MAX_ENTRIES_DEFAULT);
+ __type(key, unsigned int);
+ __type(value, unsigned long long);
+} hist_irq SEC(".maps"), hist_thread SEC(".maps"), hist_user SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, SUMMARY_FIELD_N);
+ __type(key, unsigned int);
+ __type(value, unsigned long long);
+} summary_irq SEC(".maps"), summary_thread SEC(".maps"), summary_user SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 1);
+} signal_stop_tracing SEC(".maps");
+
+/* Params to be set by rtla */
+const volatile int bucket_size = 1;
+const volatile int output_divisor = 1000;
+const volatile int entries = 256;
+const volatile int irq_threshold;
+const volatile int thread_threshold;
+const volatile bool aa_only;
+
+int stop_tracing;
+
+nosubprog unsigned long long map_get(void *map,
+ unsigned int key)
+{
+ unsigned long long *value_ptr;
+
+ value_ptr = bpf_map_lookup_elem(map, &key);
+
+ return !value_ptr ? 0 : *value_ptr;
+}
+
+nosubprog void map_set(void *map,
+ unsigned int key,
+ unsigned long long value)
+{
+ bpf_map_update_elem(map, &key, &value, BPF_ANY);
+}
+
+nosubprog void map_increment(void *map,
+ unsigned int key)
+{
+ map_set(map, key, map_get(map, key) + 1);
+}
+
+nosubprog void update_main_hist(void *map,
+ int bucket)
+{
+ if (entries == 0)
+ /* No histogram */
+ return;
+
+ if (bucket >= entries)
+ /* Overflow */
+ return;
+
+ map_increment(map, bucket);
+}
+
+nosubprog void update_summary(void *map,
+ unsigned long long latency,
+ int bucket)
+{
+ if (aa_only)
+ /* Auto-analysis only, nothing to be done here */
+ return;
+
+ map_set(map, SUMMARY_CURRENT, latency);
+
+ if (bucket >= entries)
+ /* Overflow */
+ map_increment(map, SUMMARY_OVERFLOW);
+
+ if (latency > map_get(map, SUMMARY_MAX))
+ map_set(map, SUMMARY_MAX, latency);
+
+ if (latency < map_get(map, SUMMARY_MIN) || map_get(map, SUMMARY_COUNT) == 0)
+ map_set(map, SUMMARY_MIN, latency);
+
+ map_increment(map, SUMMARY_COUNT);
+ map_set(map, SUMMARY_SUM, map_get(map, SUMMARY_SUM) + latency);
+}
+
+nosubprog void set_stop_tracing(void)
+{
+ int value = 0;
+
+ /* Suppress further sample processing */
+ stop_tracing = 1;
+
+ /* Signal to userspace */
+ bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0);
+}
+
+SEC("tp/osnoise/timerlat_sample")
+int handle_timerlat_sample(struct trace_event_raw_timerlat_sample *tp_args)
+{
+ unsigned long long latency, latency_us;
+ int bucket;
+
+ if (stop_tracing)
+ return 0;
+
+ latency = tp_args->timer_latency / output_divisor;
+ latency_us = tp_args->timer_latency / 1000;
+ bucket = latency / bucket_size;
+
+ if (tp_args->context == 0) {
+ update_main_hist(&hist_irq, bucket);
+ update_summary(&summary_irq, latency, bucket);
+
+ if (irq_threshold != 0 && latency_us >= irq_threshold)
+ set_stop_tracing();
+ } else if (tp_args->context == 1) {
+ update_main_hist(&hist_thread, bucket);
+ update_summary(&summary_thread, latency, bucket);
+
+ if (thread_threshold != 0 && latency_us >= thread_threshold)
+ set_stop_tracing();
+ } else {
+ update_main_hist(&hist_user, bucket);
+ update_summary(&summary_user, latency, bucket);
+ }
+
+ return 0;
+}
diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c
index 21cdcc5c4a29..c29e2ba2d7d8 100644
--- a/tools/tracing/rtla/src/timerlat.c
+++ b/tools/tracing/rtla/src/timerlat.c
@@ -2,6 +2,7 @@
/*
* 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>
@@ -11,9 +12,114 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
+#include <sched.h>
#include "timerlat.h"
+#define DEFAULT_TIMERLAT_PERIOD 1000 /* 1ms */
+
+/*
+ * 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;
+
+ 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, &params->monitored_cpus);
+ }
+
+ retval = osnoise_set_stop_us(tool->context, params->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);
+ if (retval) {
+ err_msg("Failed to set stop total us\n");
+ 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;
+ }
+
+ if (params->hk_cpus) {
+ retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
+ &params->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(&params->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) {
+ 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;
+ } else {
+ debug_msg("User-space interface not detected, setting kernel-threads\n");
+ params->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;
+
+out_err:
+ return -1;
+}
+
static void timerlat_usage(int err)
{
int i;
diff --git a/tools/tracing/rtla/src/timerlat.h b/tools/tracing/rtla/src/timerlat.h
index 88561bfd14f3..73045aef23fa 100644
--- a/tools/tracing/rtla/src/timerlat.h
+++ b/tools/tracing/rtla/src/timerlat.h
@@ -1,4 +1,58 @@
// SPDX-License-Identifier: GPL-2.0
+#include "osnoise.h"
+
+struct timerlat_params {
+ /* Common params */
+ char *cpus;
+ cpu_set_t monitored_cpus;
+ char *trace_output;
+ char *cgroup_name;
+ unsigned long long runtime;
+ long long stop_us;
+ long long stop_total_us;
+ 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;
+ 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);
+
int timerlat_hist_main(int argc, char *argv[]);
int timerlat_top_main(int argc, char *argv[]);
int timerlat_main(int argc, char *argv[]);
diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c
index 7bd80ee2a5b4..31e66ea2b144 100644
--- a/tools/tracing/rtla/src/timerlat_aa.c
+++ b/tools/tracing/rtla/src/timerlat_aa.c
@@ -5,8 +5,6 @@
#include <stdlib.h>
#include <errno.h>
-#include "utils.h"
-#include "osnoise.h"
#include "timerlat.h"
#include <unistd.h>
diff --git a/tools/tracing/rtla/src/timerlat_bpf.c b/tools/tracing/rtla/src/timerlat_bpf.c
new file mode 100644
index 000000000000..0bc44ce5d69b
--- /dev/null
+++ b/tools/tracing/rtla/src/timerlat_bpf.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifdef HAVE_BPF_SKEL
+#define _GNU_SOURCE
+#include "timerlat.h"
+#include "timerlat_bpf.h"
+#include "timerlat.skel.h"
+
+static struct timerlat_bpf *bpf;
+
+/*
+ * timerlat_bpf_init - load and initialize BPF program to collect timerlat data
+ */
+int timerlat_bpf_init(struct timerlat_params *params)
+{
+ int err;
+
+ debug_msg("Loading BPF program\n");
+
+ bpf = timerlat_bpf__open();
+ if (!bpf)
+ 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;
+
+ if (params->entries != 0) {
+ /* Pass histogram options */
+ bpf->rodata->bucket_size = params->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);
+ } else {
+ /* No entries, disable histogram */
+ bpf_map__set_autocreate(bpf->maps.hist_irq, false);
+ bpf_map__set_autocreate(bpf->maps.hist_thread, false);
+ bpf_map__set_autocreate(bpf->maps.hist_user, false);
+ }
+
+ if (params->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);
+ bpf_map__set_autocreate(bpf->maps.summary_user, false);
+ }
+
+ /* Load and verify BPF program */
+ err = timerlat_bpf__load(bpf);
+ if (err) {
+ timerlat_bpf__destroy(bpf);
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * timerlat_bpf_attach - attach BPF program to collect timerlat data
+ */
+int timerlat_bpf_attach(void)
+{
+ debug_msg("Attaching BPF program\n");
+
+ return timerlat_bpf__attach(bpf);
+}
+
+/*
+ * timerlat_bpf_detach - detach BPF program to collect timerlat data
+ */
+void timerlat_bpf_detach(void)
+{
+ timerlat_bpf__detach(bpf);
+}
+
+/*
+ * timerlat_bpf_detach - destroy BPF program to collect timerlat data
+ */
+void timerlat_bpf_destroy(void)
+{
+ timerlat_bpf__destroy(bpf);
+}
+
+static int handle_rb_event(void *ctx, void *data, size_t data_sz)
+{
+ return 0;
+}
+
+/*
+ * timerlat_bpf_wait - wait until tracing is stopped or signal
+ */
+int timerlat_bpf_wait(int timeout)
+{
+ struct ring_buffer *rb;
+ int retval;
+
+ rb = ring_buffer__new(bpf_map__fd(bpf->maps.signal_stop_tracing),
+ handle_rb_event, NULL, NULL);
+ retval = ring_buffer__poll(rb, timeout * 1000);
+ ring_buffer__free(rb);
+
+ return retval;
+}
+
+static int get_value(struct bpf_map *map_irq,
+ struct bpf_map *map_thread,
+ struct bpf_map *map_user,
+ int key,
+ long long *value_irq,
+ long long *value_thread,
+ long long *value_user,
+ int cpus)
+{
+ int err;
+
+ err = bpf_map__lookup_elem(map_irq, &key,
+ sizeof(unsigned int), value_irq,
+ sizeof(long long) * cpus, 0);
+ if (err)
+ return err;
+ err = bpf_map__lookup_elem(map_thread, &key,
+ sizeof(unsigned int), value_thread,
+ sizeof(long long) * cpus, 0);
+ if (err)
+ return err;
+ err = bpf_map__lookup_elem(map_user, &key,
+ sizeof(unsigned int), value_user,
+ sizeof(long long) * cpus, 0);
+ if (err)
+ return err;
+ return 0;
+}
+
+/*
+ * timerlat_bpf_get_hist_value - get value from BPF hist map
+ */
+int timerlat_bpf_get_hist_value(int key,
+ long long *value_irq,
+ long long *value_thread,
+ long long *value_user,
+ int cpus)
+{
+ return get_value(bpf->maps.hist_irq,
+ bpf->maps.hist_thread,
+ bpf->maps.hist_user,
+ key, value_irq, value_thread, value_user, cpus);
+}
+
+/*
+ * timerlat_bpf_get_summary_value - get value from BPF summary map
+ */
+int timerlat_bpf_get_summary_value(enum summary_field key,
+ long long *value_irq,
+ long long *value_thread,
+ long long *value_user,
+ int cpus)
+{
+ return get_value(bpf->maps.summary_irq,
+ bpf->maps.summary_thread,
+ bpf->maps.summary_user,
+ key, value_irq, value_thread, value_user, cpus);
+}
+#endif /* HAVE_BPF_SKEL */
diff --git a/tools/tracing/rtla/src/timerlat_bpf.h b/tools/tracing/rtla/src/timerlat_bpf.h
new file mode 100644
index 000000000000..f1b54dbddb0e
--- /dev/null
+++ b/tools/tracing/rtla/src/timerlat_bpf.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#pragma once
+
+enum summary_field {
+ SUMMARY_CURRENT,
+ SUMMARY_MIN,
+ SUMMARY_MAX,
+ SUMMARY_COUNT,
+ SUMMARY_SUM,
+ SUMMARY_OVERFLOW,
+ SUMMARY_FIELD_N
+};
+
+#ifndef __bpf__
+#ifdef HAVE_BPF_SKEL
+int timerlat_bpf_init(struct timerlat_params *params);
+int timerlat_bpf_attach(void);
+void timerlat_bpf_detach(void);
+void timerlat_bpf_destroy(void);
+int timerlat_bpf_wait(int timeout);
+int timerlat_bpf_get_hist_value(int key,
+ long long *value_irq,
+ long long *value_thread,
+ long long *value_user,
+ int cpus);
+int timerlat_bpf_get_summary_value(enum summary_field key,
+ long long *value_irq,
+ long long *value_thread,
+ long long *value_user,
+ int cpus);
+static inline int have_libbpf_support(void) { return 1; }
+#else
+static inline int timerlat_bpf_init(struct timerlat_params *params)
+{
+ return -1;
+}
+static inline int timerlat_bpf_attach(void) { return -1; }
+static inline void timerlat_bpf_detach(void) { };
+static inline void timerlat_bpf_destroy(void) { };
+static inline int timerlat_bpf_wait(int timeout) { return -1; }
+static inline int timerlat_bpf_get_hist_value(int key,
+ long long *value_irq,
+ long long *value_thread,
+ long long *value_user,
+ int cpus)
+{
+ return -1;
+}
+static inline int timerlat_bpf_get_summary_value(enum summary_field key,
+ long long *value_irq,
+ long long *value_thread,
+ long long *value_user,
+ int cpus)
+{
+ return -1;
+}
+static inline int have_libbpf_support(void) { return 0; }
+#endif /* HAVE_BPF_SKEL */
+#endif /* __bpf__ */
diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c
index 4403cc4eba30..36d2294c963d 100644
--- a/tools/tracing/rtla/src/timerlat_hist.c
+++ b/tools/tracing/rtla/src/timerlat_hist.c
@@ -14,49 +14,10 @@
#include <sched.h>
#include <pthread.h>
-#include "utils.h"
-#include "osnoise.h"
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_u.h"
-
-struct timerlat_hist_params {
- char *cpus;
- cpu_set_t monitored_cpus;
- char *trace_output;
- char *cgroup_name;
- unsigned long long runtime;
- long long stop_us;
- long long stop_total_us;
- long long timerlat_period_us;
- long long print_stack;
- int sleep_time;
- int output_divisor;
- int duration;
- int set_sched;
- int dma_latency;
- int cgroup;
- int hk_cpus;
- int no_aa;
- int dump_tasks;
- int user_workload;
- int kernel_workload;
- int user_hist;
- cpu_set_t hk_cpu_set;
- struct sched_attr sched_param;
- struct trace_events *events;
- char no_irq;
- char no_thread;
- char no_header;
- char no_summary;
- char no_index;
- char with_zeros;
- int bucket_size;
- int entries;
- int warmup;
- int buffer_size;
- int deepest_idle_state;
-};
+#include "timerlat_bpf.h"
struct timerlat_hist_cpu {
int *irq;
@@ -174,7 +135,7 @@ timerlat_hist_update(struct osnoise_tool *tool, int cpu,
unsigned long long context,
unsigned long long latency)
{
- struct timerlat_hist_params *params = tool->params;
+ struct timerlat_params *params = tool->params;
struct timerlat_hist_data *data = tool->data;
int entries = data->entries;
int bucket;
@@ -234,11 +195,94 @@ timerlat_hist_handler(struct trace_seq *s, struct tep_record *record,
}
/*
+ * timerlat_hist_bpf_pull_data - copy data from BPF maps into userspace
+ */
+static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool)
+{
+ struct timerlat_hist_data *data = tool->data;
+ int i, j, err;
+ long long value_irq[data->nr_cpus],
+ value_thread[data->nr_cpus],
+ value_user[data->nr_cpus];
+
+ /* Pull histogram */
+ for (i = 0; i < data->entries; i++) {
+ err = timerlat_bpf_get_hist_value(i, value_irq, value_thread,
+ value_user, data->nr_cpus);
+ if (err)
+ return err;
+ for (j = 0; j < data->nr_cpus; j++) {
+ data->hist[j].irq[i] = value_irq[j];
+ data->hist[j].thread[i] = value_thread[j];
+ data->hist[j].user[i] = value_user[j];
+ }
+ }
+
+ /* Pull summary */
+ err = timerlat_bpf_get_summary_value(SUMMARY_COUNT,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->hist[i].irq_count = value_irq[i];
+ data->hist[i].thread_count = value_thread[i];
+ data->hist[i].user_count = value_user[i];
+ }
+
+ err = timerlat_bpf_get_summary_value(SUMMARY_MIN,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->hist[i].min_irq = value_irq[i];
+ data->hist[i].min_thread = value_thread[i];
+ data->hist[i].min_user = value_user[i];
+ }
+
+ err = timerlat_bpf_get_summary_value(SUMMARY_MAX,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->hist[i].max_irq = value_irq[i];
+ data->hist[i].max_thread = value_thread[i];
+ data->hist[i].max_user = value_user[i];
+ }
+
+ err = timerlat_bpf_get_summary_value(SUMMARY_SUM,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->hist[i].sum_irq = value_irq[i];
+ data->hist[i].sum_thread = value_thread[i];
+ data->hist[i].sum_user = value_user[i];
+ }
+
+ err = timerlat_bpf_get_summary_value(SUMMARY_OVERFLOW,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->hist[i].irq[data->entries] = value_irq[i];
+ data->hist[i].thread[data->entries] = value_thread[i];
+ data->hist[i].user[data->entries] = value_user[i];
+ }
+
+ return 0;
+}
+
+/*
* timerlat_hist_header - print the header of the tracer to the output
*/
static void timerlat_hist_header(struct osnoise_tool *tool)
{
- struct timerlat_hist_params *params = tool->params;
+ struct timerlat_params *params = tool->params;
struct timerlat_hist_data *data = tool->data;
struct trace_seq *s = tool->trace.seq;
char duration[26];
@@ -271,7 +315,7 @@ static void timerlat_hist_header(struct osnoise_tool *tool)
if (!params->no_thread)
trace_seq_printf(s, " Thr-%03d", cpu);
- if (params->user_hist)
+ if (params->user_data)
trace_seq_printf(s, " Usr-%03d", cpu);
}
trace_seq_printf(s, "\n");
@@ -300,7 +344,7 @@ static void format_summary_value(struct trace_seq *seq,
* timerlat_print_summary - print the summary of the hist data to the output
*/
static void
-timerlat_print_summary(struct timerlat_hist_params *params,
+timerlat_print_summary(struct timerlat_params *params,
struct trace_instance *trace,
struct timerlat_hist_data *data)
{
@@ -327,7 +371,7 @@ timerlat_print_summary(struct timerlat_hist_params *params,
trace_seq_printf(trace->seq, "%9llu ",
data->hist[cpu].thread_count);
- if (params->user_hist)
+ if (params->user_data)
trace_seq_printf(trace->seq, "%9llu ",
data->hist[cpu].user_count);
}
@@ -355,7 +399,7 @@ timerlat_print_summary(struct timerlat_hist_params *params,
data->hist[cpu].min_thread,
false);
- if (params->user_hist)
+ if (params->user_data)
format_summary_value(trace->seq,
data->hist[cpu].user_count,
data->hist[cpu].min_user,
@@ -385,7 +429,7 @@ timerlat_print_summary(struct timerlat_hist_params *params,
data->hist[cpu].sum_thread,
true);
- if (params->user_hist)
+ if (params->user_data)
format_summary_value(trace->seq,
data->hist[cpu].user_count,
data->hist[cpu].sum_user,
@@ -415,7 +459,7 @@ timerlat_print_summary(struct timerlat_hist_params *params,
data->hist[cpu].max_thread,
false);
- if (params->user_hist)
+ if (params->user_data)
format_summary_value(trace->seq,
data->hist[cpu].user_count,
data->hist[cpu].max_user,
@@ -427,7 +471,7 @@ timerlat_print_summary(struct timerlat_hist_params *params,
}
static void
-timerlat_print_stats_all(struct timerlat_hist_params *params,
+timerlat_print_stats_all(struct timerlat_params *params,
struct trace_instance *trace,
struct timerlat_hist_data *data)
{
@@ -477,7 +521,7 @@ timerlat_print_stats_all(struct timerlat_hist_params *params,
if (!params->no_thread)
trace_seq_printf(trace->seq, " Thr");
- if (params->user_hist)
+ if (params->user_data)
trace_seq_printf(trace->seq, " Usr");
trace_seq_printf(trace->seq, "\n");
@@ -493,7 +537,7 @@ timerlat_print_stats_all(struct timerlat_hist_params *params,
trace_seq_printf(trace->seq, "%9llu ",
sum.thread_count);
- if (params->user_hist)
+ if (params->user_data)
trace_seq_printf(trace->seq, "%9llu ",
sum.user_count);
@@ -514,7 +558,7 @@ timerlat_print_stats_all(struct timerlat_hist_params *params,
sum.min_thread,
false);
- if (params->user_hist)
+ if (params->user_data)
format_summary_value(trace->seq,
sum.user_count,
sum.min_user,
@@ -537,7 +581,7 @@ timerlat_print_stats_all(struct timerlat_hist_params *params,
sum.sum_thread,
true);
- if (params->user_hist)
+ if (params->user_data)
format_summary_value(trace->seq,
sum.user_count,
sum.sum_user,
@@ -560,7 +604,7 @@ timerlat_print_stats_all(struct timerlat_hist_params *params,
sum.max_thread,
false);
- if (params->user_hist)
+ if (params->user_data)
format_summary_value(trace->seq,
sum.user_count,
sum.max_user,
@@ -575,7 +619,7 @@ timerlat_print_stats_all(struct timerlat_hist_params *params,
* timerlat_print_stats - print data for each CPUs
*/
static void
-timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *tool)
+timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *tool)
{
struct timerlat_hist_data *data = tool->data;
struct trace_instance *trace = &tool->trace;
@@ -610,7 +654,7 @@ timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *t
data->hist[cpu].thread[bucket]);
}
- if (params->user_hist) {
+ if (params->user_data) {
total += data->hist[cpu].user[bucket];
trace_seq_printf(trace->seq, "%9d ",
data->hist[cpu].user[bucket]);
@@ -646,7 +690,7 @@ timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *t
trace_seq_printf(trace->seq, "%9d ",
data->hist[cpu].thread[data->entries]);
- if (params->user_hist)
+ if (params->user_data)
trace_seq_printf(trace->seq, "%9d ",
data->hist[cpu].user[data->entries]);
}
@@ -656,6 +700,7 @@ timerlat_print_stats(struct timerlat_hist_params *params, struct osnoise_tool *t
timerlat_print_summary(params, trace, data);
timerlat_print_stats_all(params, trace, data);
+ osnoise_report_missed_events(tool);
}
/*
@@ -733,10 +778,10 @@ static void timerlat_hist_usage(char *usage)
/*
* timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters
*/
-static struct timerlat_hist_params
+static struct timerlat_params
*timerlat_hist_parse_args(int argc, char *argv[])
{
- struct timerlat_hist_params *params;
+ struct timerlat_params *params;
struct trace_events *tevent;
int auto_thresh;
int retval;
@@ -920,7 +965,7 @@ static struct timerlat_hist_params
params->user_workload = 1;
/* fallback: -u implies in -U */
case 'U':
- params->user_hist = 1;
+ params->user_data = 1;
break;
case '0': /* no irq */
params->no_irq = 1;
@@ -1016,97 +1061,13 @@ static struct timerlat_hist_params
* timerlat_hist_apply_config - apply the hist configs to the initialized tool
*/
static int
-timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_hist_params *params)
+timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_params *params)
{
- int retval, i;
-
- if (!params->sleep_time)
- params->sleep_time = 1;
-
- if (params->cpus) {
- retval = osnoise_set_cpus(tool->context, params->cpus);
- if (retval) {
- err_msg("Failed to apply CPUs config\n");
- goto out_err;
- }
- } else {
- for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)
- CPU_SET(i, &params->monitored_cpus);
- }
-
- if (params->stop_us) {
- retval = osnoise_set_stop_us(tool->context, params->stop_us);
- if (retval) {
- err_msg("Failed to set stop us\n");
- goto out_err;
- }
- }
-
- if (params->stop_total_us) {
- retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
- if (retval) {
- err_msg("Failed to set stop total us\n");
- goto out_err;
- }
- }
-
- if (params->timerlat_period_us) {
- retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us);
- if (retval) {
- err_msg("Failed to set timerlat period\n");
- goto out_err;
- }
- }
-
- if (params->print_stack) {
- retval = osnoise_set_print_stack(tool->context, params->print_stack);
- if (retval) {
- err_msg("Failed to set print stack\n");
- goto out_err;
- }
- }
-
- if (params->hk_cpus) {
- retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
- &params->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(&params->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_hist) {
- 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_hist = 1;
- } else {
- debug_msg("User-space interface not detected, setting kernel-threads\n");
- params->kernel_workload = 1;
- }
- }
+ int retval;
- if (params->user_hist) {
- retval = osnoise_set_workload(tool->context, 0);
- if (retval) {
- err_msg("Failed to set OSNOISE_WORKLOAD option\n");
- goto out_err;
- }
- }
+ retval = timerlat_apply_config(tool, params);
+ if (retval)
+ goto out_err;
return 0;
@@ -1118,7 +1079,7 @@ out_err:
* timerlat_init_hist - initialize a timerlat hist tool with parameters
*/
static struct osnoise_tool
-*timerlat_init_hist(struct timerlat_hist_params *params)
+*timerlat_init_hist(struct timerlat_params *params)
{
struct osnoise_tool *tool;
int nr_cpus;
@@ -1146,16 +1107,27 @@ out_err:
}
static int stop_tracing;
+static struct trace_instance *hist_inst = NULL;
static void 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
*/
static void
-timerlat_hist_set_signals(struct timerlat_hist_params *params)
+timerlat_hist_set_signals(struct timerlat_params *params)
{
signal(SIGINT, stop_hist);
if (params->duration) {
@@ -1166,17 +1138,18 @@ timerlat_hist_set_signals(struct timerlat_hist_params *params)
int timerlat_hist_main(int argc, char *argv[])
{
- struct timerlat_hist_params *params;
+ 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;
- int return_value = 1;
pthread_t timerlat_u;
int retval;
int nr_cpus, i;
+ bool no_bpf = false;
params = timerlat_hist_parse_args(argc, argv);
if (!params)
@@ -1195,6 +1168,30 @@ int timerlat_hist_main(int argc, char *argv[])
}
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;
+
+ if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) {
+ debug_msg("RTLA_NO_BPF set, disabling BPF\n");
+ no_bpf = true;
+ }
+
+ if (!no_bpf && !tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) {
+ debug_msg("osnoise:timerlat_sample missing, disabling BPF\n");
+ no_bpf = true;
+ }
+
+ if (!no_bpf) {
+ retval = timerlat_bpf_init(params);
+ if (retval) {
+ debug_msg("Could not enable BPF\n");
+ no_bpf = true;
+ }
+ }
retval = enable_timerlat(trace);
if (retval) {
@@ -1323,35 +1320,55 @@ int timerlat_hist_main(int argc, char *argv[])
trace_instance_start(&record->trace);
if (!params->no_aa)
trace_instance_start(&aa->trace);
- trace_instance_start(trace);
+ if (no_bpf) {
+ trace_instance_start(trace);
+ } else {
+ retval = timerlat_bpf_attach();
+ if (retval) {
+ err_msg("Error attaching BPF program\n");
+ goto out_hist;
+ }
+ }
tool->start_time = time(NULL);
timerlat_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 (trace_is_off(&tool->trace, &record->trace))
- break;
+ if (no_bpf) {
+ 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;
+ }
- /* is there still any user-threads ? */
- if (params->user_workload) {
- if (params_u.stopped_running) {
- debug_msg("timerlat user-space threads stopped!\n");
+ if (osnoise_trace_is_off(tool, record))
break;
+
+ /* 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
+ timerlat_bpf_wait(-1);
+
+ if (!no_bpf) {
+ 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) {
@@ -1361,18 +1378,17 @@ int timerlat_hist_main(int argc, char *argv[])
timerlat_print_stats(params, tool);
- return_value = 0;
+ return_value = PASSED;
- if (trace_is_off(&tool->trace, &record->trace)) {
+ 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);
- if (params->trace_output) {
- printf(" Saving trace to %s\n", params->trace_output);
- save_trace_to_file(record->trace.inst, params->trace_output);
- }
+ save_trace_to_file(record ? record->trace.inst : NULL,
+ params->trace_output);
+ return_value = FAILED;
}
out_hist:
@@ -1395,6 +1411,8 @@ out_free:
osnoise_destroy_tool(tool);
free(params);
free_cpu_idle_disable_states();
+ if (!no_bpf)
+ timerlat_bpf_destroy();
out_exit:
exit(return_value);
}
diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c
index 059b468981e4..7365e08fe986 100644
--- a/tools/tracing/rtla/src/timerlat_top.c
+++ b/tools/tracing/rtla/src/timerlat_top.c
@@ -15,44 +15,10 @@
#include <sched.h>
#include <pthread.h>
-#include "utils.h"
-#include "osnoise.h"
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_u.h"
-
-struct timerlat_top_params {
- char *cpus;
- cpu_set_t monitored_cpus;
- char *trace_output;
- char *cgroup_name;
- unsigned long long runtime;
- long long stop_us;
- long long stop_total_us;
- long long timerlat_period_us;
- long long print_stack;
- int sleep_time;
- int output_divisor;
- int duration;
- int quiet;
- int set_sched;
- int dma_latency;
- int no_aa;
- int aa_only;
- int dump_tasks;
- int cgroup;
- int hk_cpus;
- int user_top;
- int user_workload;
- int kernel_workload;
- int pretty_output;
- int warmup;
- int buffer_size;
- int deepest_idle_state;
- cpu_set_t hk_cpu_set;
- struct sched_attr sched_param;
- struct trace_events *events;
-};
+#include "timerlat_bpf.h"
struct timerlat_top_cpu {
unsigned long long irq_count;
@@ -162,9 +128,13 @@ 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_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 (!thread) {
cpu_data->irq_count++;
cpu_data->cur_irq = latency;
@@ -194,7 +164,7 @@ timerlat_top_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
struct trace_instance *trace = context;
- struct timerlat_top_params *params;
+ struct timerlat_params *params;
unsigned long long latency, thread;
struct osnoise_tool *top;
int cpu = record->cpu;
@@ -213,9 +183,79 @@ timerlat_top_handler(struct trace_seq *s, struct tep_record *record,
}
/*
+ * timerlat_top_bpf_pull_data - copy data from BPF maps into userspace
+ */
+static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool)
+{
+ struct timerlat_top_data *data = tool->data;
+ int i, err;
+ long long value_irq[data->nr_cpus],
+ value_thread[data->nr_cpus],
+ value_user[data->nr_cpus];
+
+ /* Pull summary */
+ err = timerlat_bpf_get_summary_value(SUMMARY_CURRENT,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->cpu_data[i].cur_irq = value_irq[i];
+ data->cpu_data[i].cur_thread = value_thread[i];
+ data->cpu_data[i].cur_user = value_user[i];
+ }
+
+ err = timerlat_bpf_get_summary_value(SUMMARY_COUNT,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->cpu_data[i].irq_count = value_irq[i];
+ data->cpu_data[i].thread_count = value_thread[i];
+ data->cpu_data[i].user_count = value_user[i];
+ }
+
+ err = timerlat_bpf_get_summary_value(SUMMARY_MIN,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->cpu_data[i].min_irq = value_irq[i];
+ data->cpu_data[i].min_thread = value_thread[i];
+ data->cpu_data[i].min_user = value_user[i];
+ }
+
+ err = timerlat_bpf_get_summary_value(SUMMARY_MAX,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->cpu_data[i].max_irq = value_irq[i];
+ data->cpu_data[i].max_thread = value_thread[i];
+ data->cpu_data[i].max_user = value_user[i];
+ }
+
+ err = timerlat_bpf_get_summary_value(SUMMARY_SUM,
+ value_irq, value_thread, value_user,
+ data->nr_cpus);
+ if (err)
+ return err;
+ for (i = 0; i < data->nr_cpus; i++) {
+ data->cpu_data[i].sum_irq = value_irq[i];
+ data->cpu_data[i].sum_thread = value_thread[i];
+ data->cpu_data[i].sum_user = value_user[i];
+ }
+
+ return 0;
+}
+
+/*
* timerlat_top_header - print the header of the tool output
*/
-static void timerlat_top_header(struct timerlat_top_params *params, struct osnoise_tool *top)
+static void timerlat_top_header(struct timerlat_params *params, struct osnoise_tool *top)
{
struct trace_seq *s = top->trace.seq;
char duration[26];
@@ -226,7 +266,7 @@ static void timerlat_top_header(struct timerlat_top_params *params, struct osnoi
trace_seq_printf(s, "\033[2;37;40m");
trace_seq_printf(s, " Timer Latency ");
- if (params->user_top)
+ if (params->user_data)
trace_seq_printf(s, " ");
if (params->pretty_output)
@@ -237,7 +277,7 @@ static void timerlat_top_header(struct timerlat_top_params *params, struct osnoi
params->output_divisor == 1 ? "ns" : "us",
params->output_divisor == 1 ? "ns" : "us");
- if (params->user_top) {
+ if (params->user_data) {
trace_seq_printf(s, " | Ret user Timer Latency (%s)",
params->output_divisor == 1 ? "ns" : "us");
}
@@ -247,7 +287,7 @@ static void timerlat_top_header(struct timerlat_top_params *params, struct osnoi
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_top)
+ if (params->user_data)
trace_seq_printf(s, " | cur min avg max");
if (params->pretty_output)
@@ -263,15 +303,11 @@ static const char *no_value = " -";
static void timerlat_top_print(struct osnoise_tool *top, int cpu)
{
- struct timerlat_top_params *params = top->params;
+ struct timerlat_params *params = top->params;
struct timerlat_top_data *data = top->data;
struct timerlat_top_cpu *cpu_data = &data->cpu_data[cpu];
- int divisor = params->output_divisor;
struct trace_seq *s = top->trace.seq;
- if (divisor == 0)
- return;
-
/*
* Skip if no data is available: is this cpu offline?
*/
@@ -286,23 +322,23 @@ static void timerlat_top_print(struct osnoise_tool *top, int cpu)
if (!cpu_data->irq_count) {
trace_seq_printf(s, "%s %s %s %s |", no_value, no_value, no_value, no_value);
} else {
- trace_seq_printf(s, "%9llu ", cpu_data->cur_irq / params->output_divisor);
- trace_seq_printf(s, "%9llu ", cpu_data->min_irq / params->output_divisor);
- trace_seq_printf(s, "%9llu ", (cpu_data->sum_irq / cpu_data->irq_count) / divisor);
- trace_seq_printf(s, "%9llu |", cpu_data->max_irq / divisor);
+ trace_seq_printf(s, "%9llu ", cpu_data->cur_irq);
+ trace_seq_printf(s, "%9llu ", cpu_data->min_irq);
+ trace_seq_printf(s, "%9llu ", cpu_data->sum_irq / cpu_data->irq_count);
+ trace_seq_printf(s, "%9llu |", cpu_data->max_irq);
}
if (!cpu_data->thread_count) {
trace_seq_printf(s, "%s %s %s %s", no_value, no_value, no_value, no_value);
} else {
- trace_seq_printf(s, "%9llu ", cpu_data->cur_thread / divisor);
- trace_seq_printf(s, "%9llu ", cpu_data->min_thread / divisor);
+ trace_seq_printf(s, "%9llu ", cpu_data->cur_thread);
+ trace_seq_printf(s, "%9llu ", cpu_data->min_thread);
trace_seq_printf(s, "%9llu ",
- (cpu_data->sum_thread / cpu_data->thread_count) / divisor);
- trace_seq_printf(s, "%9llu", cpu_data->max_thread / divisor);
+ cpu_data->sum_thread / cpu_data->thread_count);
+ trace_seq_printf(s, "%9llu", cpu_data->max_thread);
}
- if (!params->user_top) {
+ if (!params->user_data) {
trace_seq_printf(s, "\n");
return;
}
@@ -312,11 +348,11 @@ static void timerlat_top_print(struct osnoise_tool *top, int cpu)
if (!cpu_data->user_count) {
trace_seq_printf(s, "%s %s %s %s\n", no_value, no_value, no_value, no_value);
} else {
- trace_seq_printf(s, "%9llu ", cpu_data->cur_user / divisor);
- trace_seq_printf(s, "%9llu ", cpu_data->min_user / divisor);
+ trace_seq_printf(s, "%9llu ", cpu_data->cur_user);
+ trace_seq_printf(s, "%9llu ", cpu_data->min_user);
trace_seq_printf(s, "%9llu ",
- (cpu_data->sum_user / cpu_data->user_count) / divisor);
- trace_seq_printf(s, "%9llu\n", cpu_data->max_user / divisor);
+ cpu_data->sum_user / cpu_data->user_count);
+ trace_seq_printf(s, "%9llu\n", cpu_data->max_user);
}
}
@@ -327,15 +363,11 @@ static void
timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summary)
{
const char *split = "----------------------------------------";
- struct timerlat_top_params *params = top->params;
+ struct timerlat_params *params = top->params;
unsigned long long count = summary->irq_count;
- int divisor = params->output_divisor;
struct trace_seq *s = top->trace.seq;
int e = 0;
- if (divisor == 0)
- return;
-
/*
* Skip if no data is available: is this cpu offline?
*/
@@ -348,7 +380,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_top)
+ if (params->user_data)
trace_seq_printf(s, "-|%.*s", 39, split);
trace_seq_printf(s, "\n");
@@ -358,22 +390,22 @@ timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summar
trace_seq_printf(s, " %s %s %s |", no_value, no_value, no_value);
} else {
trace_seq_printf(s, " ");
- trace_seq_printf(s, "%9llu ", summary->min_irq / params->output_divisor);
- trace_seq_printf(s, "%9llu ", (summary->sum_irq / summary->irq_count) / divisor);
- trace_seq_printf(s, "%9llu |", summary->max_irq / divisor);
+ trace_seq_printf(s, "%9llu ", summary->min_irq);
+ trace_seq_printf(s, "%9llu ", summary->sum_irq / summary->irq_count);
+ trace_seq_printf(s, "%9llu |", summary->max_irq);
}
if (!summary->thread_count) {
trace_seq_printf(s, "%s %s %s %s", no_value, no_value, no_value, no_value);
} else {
trace_seq_printf(s, " ");
- trace_seq_printf(s, "%9llu ", summary->min_thread / divisor);
+ trace_seq_printf(s, "%9llu ", summary->min_thread);
trace_seq_printf(s, "%9llu ",
- (summary->sum_thread / summary->thread_count) / divisor);
- trace_seq_printf(s, "%9llu", summary->max_thread / divisor);
+ summary->sum_thread / summary->thread_count);
+ trace_seq_printf(s, "%9llu", summary->max_thread);
}
- if (!params->user_top) {
+ if (!params->user_data) {
trace_seq_printf(s, "\n");
return;
}
@@ -384,10 +416,10 @@ timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summar
trace_seq_printf(s, " %s %s %s |", no_value, no_value, no_value);
} else {
trace_seq_printf(s, " ");
- trace_seq_printf(s, "%9llu ", summary->min_user / divisor);
+ trace_seq_printf(s, "%9llu ", summary->min_user);
trace_seq_printf(s, "%9llu ",
- (summary->sum_user / summary->user_count) / divisor);
- trace_seq_printf(s, "%9llu\n", summary->max_user / divisor);
+ summary->sum_user / summary->user_count);
+ trace_seq_printf(s, "%9llu\n", summary->max_user);
}
}
@@ -404,7 +436,7 @@ static void clear_terminal(struct trace_seq *seq)
* timerlat_print_stats - print data for all cpus
*/
static void
-timerlat_print_stats(struct timerlat_top_params *params, struct osnoise_tool *top)
+timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *top)
{
struct trace_instance *trace = &top->trace;
struct timerlat_top_cpu summary;
@@ -435,6 +467,7 @@ timerlat_print_stats(struct timerlat_top_params *params, struct osnoise_tool *to
trace_seq_do_printf(trace->seq);
trace_seq_reset(trace->seq);
+ osnoise_report_missed_events(top);
}
/*
@@ -504,10 +537,10 @@ static void timerlat_top_usage(char *usage)
/*
* timerlat_top_parse_args - allocs, parse and fill the cmd line parameters
*/
-static struct timerlat_top_params
+static struct timerlat_params
*timerlat_top_parse_args(int argc, char **argv)
{
- struct timerlat_top_params *params;
+ struct timerlat_params *params;
struct trace_events *tevent;
long long auto_thresh;
int retval;
@@ -689,7 +722,7 @@ static struct timerlat_top_params
params->user_workload = true;
/* fallback: -u implies -U */
case 'U':
- params->user_top = true;
+ params->user_data = true;
break;
case '0': /* trigger */
if (params->events) {
@@ -764,100 +797,13 @@ static struct timerlat_top_params
* timerlat_top_apply_config - apply the top configs to the initialized tool
*/
static int
-timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_top_params *params)
+timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_params *params)
{
int retval;
- int i;
-
- if (!params->sleep_time)
- params->sleep_time = 1;
-
- if (params->cpus) {
- retval = osnoise_set_cpus(top->context, params->cpus);
- if (retval) {
- err_msg("Failed to apply CPUs config\n");
- goto out_err;
- }
- } else {
- for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)
- CPU_SET(i, &params->monitored_cpus);
- }
-
- if (params->stop_us) {
- retval = osnoise_set_stop_us(top->context, params->stop_us);
- if (retval) {
- err_msg("Failed to set stop us\n");
- goto out_err;
- }
- }
- if (params->stop_total_us) {
- retval = osnoise_set_stop_total_us(top->context, params->stop_total_us);
- if (retval) {
- err_msg("Failed to set stop total us\n");
- goto out_err;
- }
- }
-
-
- if (params->timerlat_period_us) {
- retval = osnoise_set_timerlat_period_us(top->context, params->timerlat_period_us);
- if (retval) {
- err_msg("Failed to set timerlat period\n");
- goto out_err;
- }
- }
-
-
- if (params->print_stack) {
- retval = osnoise_set_print_stack(top->context, params->print_stack);
- if (retval) {
- err_msg("Failed to set print stack\n");
- goto out_err;
- }
- }
-
- if (params->hk_cpus) {
- retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
- &params->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(&params->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_top) {
- 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_top = 1;
- } else {
- debug_msg("User-space interface not detected, setting kernel-threads\n");
- params->kernel_workload = 1;
- }
- }
-
- if (params->user_top) {
- retval = osnoise_set_workload(top->context, 0);
- if (retval) {
- err_msg("Failed to set OSNOISE_WORKLOAD option\n");
- goto out_err;
- }
- }
+ retval = timerlat_apply_config(top, params);
+ if (retval)
+ goto out_err;
if (isatty(STDOUT_FILENO) && !params->quiet)
params->pretty_output = 1;
@@ -872,7 +818,7 @@ out_err:
* timerlat_init_top - initialize a timerlat top tool with parameters
*/
static struct osnoise_tool
-*timerlat_init_top(struct timerlat_top_params *params)
+*timerlat_init_top(struct timerlat_params *params)
{
struct osnoise_tool *top;
int nr_cpus;
@@ -900,16 +846,27 @@ out_err:
}
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_top_params *params)
+timerlat_top_set_signals(struct timerlat_params *params)
{
signal(SIGINT, stop_top);
if (params->duration) {
@@ -918,20 +875,126 @@ timerlat_top_set_signals(struct timerlat_top_params *params)
}
}
+/*
+ * timerlat_top_main_loop - main loop to process events
+ */
+static int
+timerlat_top_main_loop(struct osnoise_tool *top,
+ struct osnoise_tool *record,
+ 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))
+ break;
+
+ /* 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 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;
+ }
+
+ if (params->quiet) {
+ /* Quiet mode: wait for stop and then, print results */
+ timerlat_bpf_wait(-1);
+
+ retval = timerlat_top_bpf_pull_data(top);
+ if (retval) {
+ err_msg("Error pulling BPF data\n");
+ return retval;
+ }
+
+ return 0;
+ }
+
+ /* Pull and display data in a loop */
+ while (!stop_tracing) {
+ wait_retval = timerlat_bpf_wait(params->sleep_time);
+
+ retval = timerlat_top_bpf_pull_data(top);
+ if (retval) {
+ err_msg("Error pulling BPF data\n");
+ return retval;
+ }
+
+ timerlat_print_stats(params, top);
+
+ if (wait_retval == 1)
+ /* Stopping requested by tracer */
+ break;
+
+ /* 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_top_params *params;
+ 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;
- int return_value = 1;
char *max_lat;
int retval;
int nr_cpus, i;
+ bool no_bpf = false;
params = timerlat_top_parse_args(argc, argv);
if (!params)
@@ -950,6 +1013,30 @@ int timerlat_top_main(int argc, char *argv[])
}
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;
+
+ if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) {
+ debug_msg("RTLA_NO_BPF set, disabling BPF\n");
+ no_bpf = true;
+ }
+
+ if (!no_bpf && !tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) {
+ debug_msg("osnoise:timerlat_sample missing, disabling BPF\n");
+ no_bpf = true;
+ }
+
+ if (!no_bpf) {
+ retval = timerlat_bpf_init(params);
+ if (retval) {
+ debug_msg("Could not enable BPF\n");
+ no_bpf = true;
+ }
+ }
retval = enable_timerlat(trace);
if (retval) {
@@ -965,7 +1052,7 @@ int timerlat_top_main(int argc, char *argv[])
}
}
- if (params->cgroup && !params->user_top) {
+ 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");
@@ -1024,15 +1111,9 @@ int timerlat_top_main(int argc, char *argv[])
}
if (!params->no_aa) {
- if (params->aa_only) {
- /* as top is not used for display, use it for aa */
- aa = top;
- } else {
- /* otherwise, a new instance is needed */
- aa = osnoise_init_tool("timerlat_aa");
- if (!aa)
- goto out_top;
- }
+ aa = osnoise_init_tool("timerlat_aa");
+ if (!aa)
+ goto out_top;
retval = timerlat_aa_init(aa, params->dump_tasks);
if (retval) {
@@ -1083,44 +1164,31 @@ int timerlat_top_main(int argc, char *argv[])
*/
if (params->trace_output)
trace_instance_start(&record->trace);
- if (!params->no_aa && aa != top)
+ if (!params->no_aa)
trace_instance_start(&aa->trace);
- trace_instance_start(trace);
+ if (no_bpf) {
+ trace_instance_start(trace);
+ } 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);
- while (!stop_tracing) {
- sleep(params->sleep_time);
+ if (no_bpf)
+ retval = timerlat_top_main_loop(top, record, params, &params_u);
+ else
+ retval = timerlat_top_bpf_main_loop(top, record, params, &params_u);
- if (params->aa_only && !trace_is_off(&top->trace, &record->trace))
- continue;
+ if (retval)
+ goto out_top;
- 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)
- timerlat_print_stats(params, top);
-
- if (trace_is_off(&top->trace, &record->trace))
- break;
-
- /* is there still any user-threads ? */
- if (params->user_workload) {
- if (params_u.stopped_running) {
- debug_msg("timerlat user space threads stopped!\n");
- break;
- }
- }
- }
+ if (!no_bpf)
+ timerlat_bpf_detach();
if (params->user_workload && !params_u.stopped_running) {
params_u.should_run = 0;
@@ -1129,18 +1197,17 @@ int timerlat_top_main(int argc, char *argv[])
timerlat_print_stats(params, top);
- return_value = 0;
+ return_value = PASSED;
- if (trace_is_off(&top->trace, &record->trace)) {
+ 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);
- if (params->trace_output) {
- printf(" Saving trace to %s\n", params->trace_output);
- save_trace_to_file(record->trace.inst, params->trace_output);
- }
+ save_trace_to_file(record ? record->trace.inst : NULL,
+ params->trace_output);
+ return_value = FAILED;
} else if (params->aa_only) {
/*
* If the trace did not stop with --aa-only, at least print the
diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c
index 170a706248ab..69cbc48d53d3 100644
--- a/tools/tracing/rtla/src/trace.c
+++ b/tools/tracing/rtla/src/trace.c
@@ -75,12 +75,16 @@ int save_trace_to_file(struct tracefs_instance *inst, const char *filename)
int out_fd, in_fd;
int retval = -1;
+ if (!inst || !filename)
+ return 0;
+
in_fd = tracefs_instance_file_open(inst, file, O_RDONLY);
if (in_fd < 0) {
err_msg("Failed to open trace file\n");
return -1;
}
+ printf(" Saving trace to %s\n", filename);
out_fd = creat(filename, mode);
if (out_fd < 0) {
err_msg("Failed to create output file %s\n", filename);
@@ -118,6 +122,8 @@ collect_registered_events(struct tep_event *event, struct tep_record *record,
struct trace_instance *trace = context;
struct trace_seq *s = trace->seq;
+ trace->processed_events++;
+
if (!event->handler)
return 0;
@@ -127,6 +133,31 @@ collect_registered_events(struct tep_event *event, struct tep_record *record,
}
/*
+ * collect_missed_events - record number of missed events
+ *
+ * If rtla cannot keep up with events generated by tracer, events are going
+ * to fall out of the ring buffer.
+ * Collect how many events were missed so it can be reported to the user.
+ */
+static int
+collect_missed_events(struct tep_event *event, struct tep_record *record,
+ int cpu, void *context)
+{
+ struct trace_instance *trace = context;
+
+ if (trace->missed_events == UINT64_MAX)
+ return 0;
+
+ if (record->missed_events > 0)
+ trace->missed_events += record->missed_events;
+ else
+ /* Events missed but no data on how many */
+ trace->missed_events = UINT64_MAX;
+
+ return 0;
+}
+
+/*
* trace_instance_destroy - destroy and free a rtla trace instance
*/
void trace_instance_destroy(struct trace_instance *trace)
@@ -181,6 +212,17 @@ int trace_instance_init(struct trace_instance *trace, char *tool_name)
*/
tracefs_trace_off(trace->inst);
+ /*
+ * Collect the number of events missed due to tracefs buffer
+ * overflow.
+ */
+ trace->missed_events = 0;
+ tracefs_follow_missed_events(trace->inst,
+ collect_missed_events,
+ trace);
+
+ trace->processed_events = 0;
+
return 0;
out_err:
@@ -197,6 +239,14 @@ int trace_instance_start(struct trace_instance *trace)
}
/*
+ * trace_instance_stop - stop tracing a given rtla instance
+ */
+int trace_instance_stop(struct trace_instance *trace)
+{
+ return tracefs_trace_off(trace->inst);
+}
+
+/*
* trace_events_free - free a list of trace events
*/
static void trace_events_free(struct trace_events *events)
@@ -522,25 +572,6 @@ void trace_events_destroy(struct trace_instance *instance,
trace_events_free(events);
}
-int trace_is_off(struct trace_instance *tool, struct trace_instance *trace)
-{
- /*
- * The tool instance is always present, it is the one used to collect
- * data.
- */
- if (!tracefs_trace_is_on(tool->inst))
- return 1;
-
- /*
- * The trace instance is only enabled when -t is set. IOW, when the system
- * is tracing.
- */
- if (trace && !tracefs_trace_is_on(trace->inst))
- return 1;
-
- return 0;
-}
-
/*
* trace_set_buffer_size - set the per-cpu tracing buffer size.
*/
diff --git a/tools/tracing/rtla/src/trace.h b/tools/tracing/rtla/src/trace.h
index c7c92dc9a18a..3cd40dd3f06c 100644
--- a/tools/tracing/rtla/src/trace.h
+++ b/tools/tracing/rtla/src/trace.h
@@ -17,10 +17,13 @@ struct trace_instance {
struct tracefs_instance *inst;
struct tep_handle *tep;
struct trace_seq *seq;
+ unsigned long long missed_events;
+ unsigned long long processed_events;
};
int trace_instance_init(struct trace_instance *trace, char *tool_name);
int trace_instance_start(struct trace_instance *trace);
+int trace_instance_stop(struct trace_instance *trace);
void trace_instance_destroy(struct trace_instance *trace);
struct trace_seq *get_trace_seq(void);
@@ -47,5 +50,4 @@ int trace_events_enable(struct trace_instance *instance,
int trace_event_add_filter(struct trace_events *event, char *filter);
int trace_event_add_trigger(struct trace_events *event, char *trigger);
-int trace_is_off(struct trace_instance *tool, struct trace_instance *trace);
int trace_set_buffer_size(struct trace_instance *trace, int size);
diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c
index 4995d35cf3ec..d6ab15dcb490 100644
--- a/tools/tracing/rtla/src/utils.c
+++ b/tools/tracing/rtla/src/utils.c
@@ -227,6 +227,8 @@ long parse_ns_duration(char *val)
# define __NR_sched_setattr 355
# elif __s390x__
# define __NR_sched_setattr 345
+# elif __loongarch__
+# define __NR_sched_setattr 274
# endif
#endif
diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h
index 101d4799a009..a2a6f89f342d 100644
--- a/tools/tracing/rtla/src/utils.h
+++ b/tools/tracing/rtla/src/utils.h
@@ -83,3 +83,9 @@ int auto_house_keeping(cpu_set_t *monitored_cpus);
#define ns_to_usf(x) (((double)x/1000))
#define ns_to_per(total, part) ((part * 100) / (double)total)
+
+enum result {
+ PASSED = 0, /* same as EXIT_SUCCESS */
+ ERROR = 1, /* same as EXIT_FAILURE, an error in arguments */
+ FAILED = 2, /* test hit the stop tracing condition */
+};
diff --git a/tools/tracing/rtla/tests/engine.sh b/tools/tracing/rtla/tests/engine.sh
new file mode 100644
index 000000000000..f2616a8e4179
--- /dev/null
+++ b/tools/tracing/rtla/tests/engine.sh
@@ -0,0 +1,117 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+test_begin() {
+ # Count tests to allow the test harness to double-check if all were
+ # included correctly.
+ ctr=0
+ [ -z "$RTLA" ] && RTLA="./rtla"
+ [ -n "$TEST_COUNT" ] && echo "1..$TEST_COUNT"
+}
+
+reset_osnoise() {
+ # Reset osnoise options to default and remove any dangling instances created
+ # by improperly exited rtla runs.
+ pushd /sys/kernel/tracing || return 1
+
+ # Remove dangling instances created by previous rtla run
+ echo 0 > tracing_thresh
+ cd instances
+ for i in osnoise_top osnoise_hist timerlat_top timerlat_hist
+ do
+ [ ! -d "$i" ] && continue
+ rmdir "$i"
+ done
+
+ # Reset options to default
+ # Note: those are copied from the default values of osnoise_data
+ # in kernel/trace/trace_osnoise.c
+ cd ../osnoise
+ echo all > cpus
+ echo DEFAULTS > options
+ echo 1000000 > period_us
+ echo 0 > print_stack
+ echo 1000000 > runtime_us
+ echo 0 > stop_tracing_total_us
+ echo 0 > stop_tracing_us
+ echo 1000 > timerlat_period_us
+
+ popd
+}
+
+check() {
+ expected_exitcode=${3:-0}
+ # 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))
+ if [ -n "$TEST_COUNT" ]
+ then
+ # Reset osnoise options before running test.
+ [ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise
+ # Run rtla; in case of failure, include its output as comment
+ # in the test results.
+ result=$(stdbuf -oL $TIMEOUT "$RTLA" $2 2>&1); exitcode=$?
+ if [ $exitcode -eq $expected_exitcode ]
+ then
+ echo "ok $ctr - $1"
+ else
+ echo "not ok $ctr - $1"
+ # Add rtla output and exit code as comments in case of failure
+ echo "$result" | col -b | while read line; do echo "# $line"; done
+ printf "#\n# exit code %s\n" $exitcode
+ fi
+ fi
+}
+
+check_with_osnoise_options() {
+ # Do the same as "check", but with pre-set osnoise options.
+ # Note: rtla should reset the osnoise options, this is used to test
+ # if it indeed does so.
+ # Save original arguments
+ arg1=$1
+ arg2=$2
+ arg3=$3
+
+ # Apply osnoise options (if not dry run)
+ if [ -n "$TEST_COUNT" ]
+ then
+ [ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise
+ shift
+ shift
+ while shift
+ do
+ [ "$1" == "" ] && continue
+ option=$(echo $1 | cut -d '=' -f 1)
+ value=$(echo $1 | cut -d '=' -f 2)
+ echo "option: $option, value: $value"
+ echo "$value" > "/sys/kernel/tracing/osnoise/$option" || return 1
+ done
+ fi
+
+ NO_RESET_OSNOISE=1 check "$arg1" "$arg2" "$arg3"
+}
+
+set_timeout() {
+ TIMEOUT="timeout -v -k 15s $1"
+}
+
+unset_timeout() {
+ unset TIMEOUT
+}
+
+set_no_reset_osnoise() {
+ NO_RESET_OSNOISE=1
+}
+
+unset_no_reset_osnoise() {
+ unset NO_RESET_OSNOISE
+}
+
+test_end() {
+ # If running without TEST_COUNT, tests are not actually run, just
+ # counted. In that case, re-run the test with the correct count.
+ [ -z "$TEST_COUNT" ] && TEST_COUNT=$ctr exec bash $0 || true
+}
+
+# Avoid any environmental discrepancies
+export LC_ALL=C
+unset_timeout
diff --git a/tools/tracing/rtla/tests/hwnoise.t b/tools/tracing/rtla/tests/hwnoise.t
new file mode 100644
index 000000000000..5f71401a139e
--- /dev/null
+++ b/tools/tracing/rtla/tests/hwnoise.t
@@ -0,0 +1,21 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+source tests/engine.sh
+test_begin
+
+set_timeout 2m
+
+check "verify help page" \
+ "hwnoise --help"
+check "detect noise higher than one microsecond" \
+ "hwnoise -c 0 -T 1 -d 5s -q"
+check "set the automatic trace mode" \
+ "hwnoise -a 5 -d 30s" 2
+check "set scheduling param to the osnoise tracer threads" \
+ "hwnoise -P F:1 -c 0 -r 900000 -d 1M -q"
+check "stop the trace if a single sample is higher than 1 us" \
+ "hwnoise -s 1 -T 1 -t -d 30s" 2
+check "enable a trace event trigger" \
+ "hwnoise -t -e osnoise:irq_noise trigger=\"hist:key=desc,duration:sort=desc,duration:vals=hitcount\" -d 1m"
+
+test_end
diff --git a/tools/tracing/rtla/tests/osnoise.t b/tools/tracing/rtla/tests/osnoise.t
new file mode 100644
index 000000000000..44908fc01abf
--- /dev/null
+++ b/tools/tracing/rtla/tests/osnoise.t
@@ -0,0 +1,25 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+source tests/engine.sh
+test_begin
+
+set_timeout 2m
+
+check "verify help page" \
+ "osnoise --help"
+check "verify the --priority/-P param" \
+ "osnoise top -P F:1 -c 0 -r 900000 -d 1M -q"
+check "verify the --stop/-s param" \
+ "osnoise top -s 30 -T 1 -t" 2
+check "verify the --trace param" \
+ "osnoise hist -s 30 -T 1 -t" 2
+check "verify the --entries/-E param" \
+ "osnoise hist -P F:1 -c 0 -r 900000 -d 1M -b 10 -E 25"
+
+# Test setting default period by putting an absurdly high period
+# and stopping on threshold.
+# If default period is not set, this will time out.
+check_with_osnoise_options "apply default period" \
+ "osnoise hist -s 1" 2 period_us=600000000
+
+test_end
diff --git a/tools/tracing/rtla/tests/timerlat.t b/tools/tracing/rtla/tests/timerlat.t
new file mode 100644
index 000000000000..579c12a85e8f
--- /dev/null
+++ b/tools/tracing/rtla/tests/timerlat.t
@@ -0,0 +1,41 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+source tests/engine.sh
+test_begin
+
+set_timeout 2m
+timerlat_sample_event='/sys/kernel/tracing/events/osnoise/timerlat_sample'
+
+if ldd $RTLA | grep libbpf >/dev/null && [ -d "$timerlat_sample_event" ]
+then
+ # rtla build with BPF and system supports BPF mode
+ no_bpf_options='0 1'
+else
+ no_bpf_options='1'
+fi
+
+# Do every test with and without BPF
+for option in $no_bpf_options
+do
+export RTLA_NO_BPF=$option
+check "verify help page" \
+ "timerlat --help"
+check "verify -s/--stack" \
+ "timerlat top -s 3 -T 10 -t" 2
+check "verify -P/--priority" \
+ "timerlat top -P F:1 -c 0 -d 1M -q"
+check "test in nanoseconds" \
+ "timerlat top -i 2 -c 0 -n -d 30s" 2
+check "set the automatic trace mode" \
+ "timerlat top -a 5 --dump-tasks" 2
+check "print the auto-analysis if hits the stop tracing condition" \
+ "timerlat top --aa-only 5" 2
+check "disable auto-analysis" \
+ "timerlat top -s 3 -T 10 -t --no-aa" 2
+check "verify -c/--cpus" \
+ "timerlat hist -c 0 -d 30s"
+check "hist test in nanoseconds" \
+ "timerlat hist -i 2 -c 0 -n -d 30s" 2
+done
+
+test_end