tracing tooling updates for 6.15:

- Allow RTLA to collect data via BPF
 
   The current implementation of rtla uses libtracefs and libtraceevent to
   pull sample events generated by the timerlat tracer from the trace
   buffer. rtla then processes the sample by updating the histogram and
   summary (current, maximum, minimum, and sum values) as well as checks
   if tracing has been stopped due to threshold overflow.
 
   In use cases where a large number of samples is being generated, that
   is, with measurements running on many CPUs and with a low interval,
   this sample processing design causes a significant CPU load on the rtla
   side. Furthermore, with >100 CPUs and 100us interval, rtla was reported
   as not being able to keep up with the samples and dropping most of them,
   leading to it being unusable.
 
   Change the way the timerlat trace processes samples by attaching
   a BPF program to the trace event using the BPF skeleton feature of bpftool.
   Unlike the current implementation, the BPF implementation does not check
   whether tracing is stopped (in BPF mode, tracing is always off to improve
   performance), but waits for a write to a BPF ringbuffer instead. This allows
   rtla to exit immediately when a threshold is violated, without waiting
   for the next iteration of the while loop.
 
   If the requirements for the BPF implementation are not met, either at
   build time or at run time, the current implementation is used as
   fallback. Which implementation is being used can be seen when running
   rtla timerlat with "-D" option. rtla can be forced to run in non-BPF
   mode by setting the RTLA_NO_BPF option to 1, for debugging purposes.
 
 - Fix LD_FLAGS from being dropped in build
 
 - Refactor code to remove duplication of save_trace_to_file
 
 - Always set options and do not rely on default settings
 
   Do not rely on the default kernel settings of the tracers when
   starting. They could have been changed by the user which gives
   inconsistent results. Always set the options that rtla expects.
 
 - Add creation of ctags and TAGS for traversing code
 -----BEGIN PGP SIGNATURE-----
 
 iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCZ+WBgRQccm9zdGVkdEBn
 b29kbWlzLm9yZwAKCRAp5XQQmuv6qg54AQDCOChaSSBiUkD0VoPKIeDMlPfvO5Qz
 Xvrst5gtopfKFgEA12/9Lll/sh1eoc4saeGBooNY48HBUMjmX3KNFB14PQg=
 =aHFu
 -----END PGP SIGNATURE-----

Merge tag 'trace-tools-v6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull tracing tooling updates from Steven Rostedt:

 - Allow RTLA to collect data via BPF

   The current implementation of rtla uses libtracefs and libtraceevent
   to pull sample events generated by the timerlat tracer from the trace
   buffer. rtla then processes the sample by updating the histogram and
   summary (current, maximum, minimum, and sum values) as well as checks
   if tracing has been stopped due to threshold overflow.

   In use cases where a large number of samples is being generated, that
   is, with measurements running on many CPUs and with a low interval,
   this sample processing design causes a significant CPU load on the
   rtla side. Furthermore, with >100 CPUs and 100us interval, rtla was
   reported as not being able to keep up with the samples and dropping
   most of them, leading to it being unusable.

   Change the way the timerlat trace processes samples by attaching a
   BPF program to the trace event using the BPF skeleton feature of
   bpftool. Unlike the current implementation, the BPF implementation
   does not check whether tracing is stopped (in BPF mode, tracing is
   always off to improve performance), but waits for a write to a BPF
   ringbuffer instead. This allows rtla to exit immediately when a
   threshold is violated, without waiting for the next iteration of the
   while loop.

   If the requirements for the BPF implementation are not met, either at
   build time or at run time, the current implementation is used as
   fallback. Which implementation is being used can be seen when running
   rtla timerlat with "-D" option. rtla can be forced to run in non-BPF
   mode by setting the RTLA_NO_BPF option to 1, for debugging purposes.

 - Fix LD_FLAGS from being dropped in build

 - Refactor code to remove duplication of save_trace_to_file

 - Always set options and do not rely on default settings

   Do not rely on the default kernel settings of the tracers when
   starting. They could have been changed by the user which gives
   inconsistent results. Always set the options that rtla expects.

 - Add creation of ctags and TAGS for traversing code

* tag 'trace-tools-v6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  rtla: Add the ability to create ctags and etags
  rtla/tests: Test setting default options
  rtla/tests: Reset osnoise options before check
  rtla: Always set all tracer options
  rtla/osnoise: Set OSNOISE_WORKLOAD to true
  rtla: Unify apply_config between top and hist
  rtla/osnoise: Unify params struct
  rtla: Fix segfault in save_trace_to_file call
  tools/build: Use SYSTEM_BPFTOOL for system bpftool
  rtla: Refactor save_trace_to_file
  tools/rv: Keep user LDFLAGS in build
  rtla/timerlat: Test BPF mode
  rtla/timerlat_top: Use BPF to collect samples
  rtla/timerlat_top: Move divisor to update
  rtla/timerlat_hist: Use BPF to collect samples
  rtla/timerlat: Add BPF skeleton to collect samples
  rtla: Add optional dependency on BPF tooling
  tools/build: Add bpftool-skeletons feature test
  rtla/timerlat: Unify params struct
This commit is contained in:
Linus Torvalds 2025-03-27 17:03:01 -07:00
commit 4fa118e5b7
25 changed files with 1316 additions and 626 deletions

View File

@ -135,7 +135,8 @@ FEATURE_TESTS_EXTRA := \
libbpf-bpf_create_map \
libpfm4 \
libdebuginfod \
clang-bpf-co-re
clang-bpf-co-re \
bpftool-skeletons
FEATURE_TESTS ?= $(FEATURE_TESTS_BASIC)

View File

@ -418,6 +418,9 @@ $(OUTPUT)test-file-handle.bin:
$(OUTPUT)test-libpfm4.bin:
$(BUILD) -lpfm
$(OUTPUT)test-bpftool-skeletons.bin:
$(SYSTEM_BPFTOOL) version | grep '^features:.*skeletons' \
> $(@:.bin=.make.output) 2>&1
###############################
clean:

View File

@ -91,6 +91,9 @@ LLVM_CONFIG ?= llvm-config
LLVM_OBJCOPY ?= llvm-objcopy
LLVM_STRIP ?= llvm-strip
# Some tools require bpftool
SYSTEM_BPFTOOL ?= bpftool
ifeq ($(CC_NO_CLANG), 1)
EXTRA_WARNINGS += -Wstrict-aliasing=3

View File

@ -4,3 +4,4 @@ rtla-static
fixdep
feature
FEATURE-DUMP
*.skel.h

View File

@ -33,9 +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
FEATURE_DISPLAY += libbpf
FEATURE_DISPLAY += clang-bpf-co-re
FEATURE_DISPLAY += bpftool-skeletons
all: $(RTLA)
@ -61,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)
@ -71,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
@ -79,6 +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
$(Q)rm -f src/timerlat.bpf.o src/timerlat.skel.h
check: $(RTLA)
RTLA=$(RTLA) prove -o -f tests/
.PHONY: FORCE clean check

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
@ -1115,6 +1119,86 @@ osnoise_report_missed_events(struct osnoise_tool *tool)
}
}
/*
* 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;

View File

@ -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.
*/
@ -106,6 +155,7 @@ 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);

View File

@ -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;
@ -508,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;
@ -731,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;
@ -808,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;
@ -842,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) {
@ -853,7 +763,7 @@ 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;
struct trace_instance *trace;
@ -983,10 +893,8 @@ int osnoise_hist_main(int argc, char *argv[])
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);
}
out_hist:

View File

@ -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;
@ -286,7 +251,7 @@ osnoise_print_stats(struct osnoise_top_params *params, struct osnoise_tool *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;
@ -354,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;
@ -553,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);
@ -610,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;
@ -640,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;
@ -674,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) {
@ -685,7 +591,7 @@ 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;
struct trace_instance *trace;
@ -813,10 +719,8 @@ int osnoise_top_main(int argc, char **argv)
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);
}
out_top:

View File

@ -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;
}

View File

@ -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;

View File

@ -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[]);

View File

@ -5,8 +5,6 @@
#include <stdlib.h>
#include <errno.h>
#include "utils.h"
#include "osnoise.h"
#include "timerlat.h"
#include <unistd.h>

View File

@ -0,0 +1,166 @@
// SPDX-License-Identifier: GPL-2.0
#ifdef HAVE_BPF_SKEL
#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 */

View File

@ -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__ */

View File

@ -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;
@ -233,12 +194,95 @@ timerlat_hist_handler(struct trace_seq *s, struct tep_record *record,
return 0;
}
/*
* 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]);
}
@ -734,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;
@ -921,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;
@ -1017,100 +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;
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;
}
} 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;
}
}
/*
* 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");
retval = timerlat_apply_config(tool, params);
if (retval)
goto out_err;
}
return 0;
@ -1122,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;
@ -1170,7 +1127,7 @@ static void stop_hist(int sig)
* 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) {
@ -1181,7 +1138,7 @@ 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;
struct osnoise_tool *tool = NULL;
@ -1192,6 +1149,7 @@ int timerlat_hist_main(int argc, char *argv[])
pthread_t timerlat_u;
int retval;
int nr_cpus, i;
bool no_bpf = false;
params = timerlat_hist_parse_args(argc, argv);
if (!params)
@ -1217,6 +1175,24 @@ int timerlat_hist_main(int argc, char *argv[])
*/
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) {
err_msg("Failed to enable timerlat tracer\n");
@ -1344,34 +1320,54 @@ 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);
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;
}
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;
retval = tracefs_iterate_raw_events(trace->tep,
trace->inst,
NULL,
0,
collect_registered_events,
trace);
if (retval < 0) {
err_msg("Error iterating on events\n");
goto out_hist;
}
if (osnoise_trace_is_off(tool, record))
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;
}
}
@ -1390,10 +1386,8 @@ int timerlat_hist_main(int argc, char *argv[])
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);
}
out_hist:
@ -1416,6 +1410,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);
}

View File

@ -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;
@ -212,10 +182,80 @@ timerlat_top_handler(struct trace_seq *s, struct tep_record *record,
return 0;
}
/*
* 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;
@ -505,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;
@ -690,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) {
@ -765,103 +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;
}
}
/*
* 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(top->context, params->kernel_workload);
if (retval < -1) {
err_msg("Failed to set OSNOISE_WORKLOAD option\n");
retval = timerlat_apply_config(top, params);
if (retval)
goto out_err;
}
if (isatty(STDOUT_FILENO) && !params->quiet)
params->pretty_output = 1;
@ -876,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;
@ -924,7 +866,7 @@ static void stop_top(int sig)
* 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) {
@ -933,9 +875,114 @@ 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;
struct osnoise_tool *top = NULL;
@ -947,6 +994,7 @@ int timerlat_top_main(int argc, char *argv[])
char *max_lat;
int retval;
int nr_cpus, i;
bool no_bpf = false;
params = timerlat_top_parse_args(argc, argv);
if (!params)
@ -972,6 +1020,23 @@ int timerlat_top_main(int argc, char *argv[])
*/
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) {
@ -987,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");
@ -1046,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) {
@ -1105,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 && !osnoise_trace_is_off(top, record))
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 (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;
}
}
}
if (!no_bpf)
timerlat_bpf_detach();
if (params->user_workload && !params_u.stopped_running) {
params_u.should_run = 0;
@ -1159,10 +1205,8 @@ int timerlat_top_main(int argc, char *argv[])
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);
} else if (params->aa_only) {
/*
* If the trace did not stop with --aa-only, at least print the

View File

@ -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);

View File

@ -8,12 +8,44 @@ test_begin() {
[ -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() {
# 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=$?
@ -29,6 +61,32 @@ check() {
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
# Apply osnoise options (if not dry run)
if [ -n "$TEST_COUNT" ]
then
[ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise
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"
}
set_timeout() {
TIMEOUT="timeout -v -k 15s $1"
}
@ -37,6 +95,14 @@ 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.

View File

@ -16,4 +16,10 @@ check "verify the --trace param" \
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" period_us=600000000
test_end

View File

@ -4,7 +4,20 @@ 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" \
@ -23,5 +36,6 @@ check "verify -c/--cpus" \
"timerlat hist -c 0 -d 30s"
check "hist test in nanoseconds" \
"timerlat hist -i 2 -c 0 -n -d 30s"
done
test_end

View File

@ -27,7 +27,7 @@ endif
INCLUDE := -Iinclude/
CFLAGS := -g -DVERSION=\"$(VERSION)\" $(FOPTS) $(WOPTS) $(EXTRA_CFLAGS) $(INCLUDE)
LDFLAGS := -ggdb $(EXTRA_LDFLAGS)
LDFLAGS := -ggdb $(LDFLAGS) $(EXTRA_LDFLAGS)
INSTALL := install
MKDIR := mkdir