mirror of
https://github.com/torvalds/linux.git
synced 2026-03-07 23:04:33 +01:00
RTLA patches for v7.0
- Remove unused function declarations Some functions were removed in recent code consolidation 6.18, but their prototypes were not removed from headers. Remove them. - Set stop threshold after enabling instances Prefer recording samples without stopping on them on the start of tracing to stopping on samples that are never recorded. This fixes flakiness of some RTLA tests and unifies behavior of sample collection between tracefs mode and BPF mode. - Consolidate usage help message implementation RTLA tools (osnoise-top, osnoise-hist, timerlat-top, timerlat-hist) each implement usage help individually. Move common logic between them into a new function to reduce code duplication. - Add BPF actions feature Add option --bpf-action to attach a BPF program (passed as filename of its ELF representation) to be executed via BPF tail call at latency threshold. - Consolidate command line option parsing Each RTLA tool implements the parsing of command line options individually. Now that we have a common structure for parameters, unify the parsing of those options common among all four tools into one function. - De-duplicate cgroup common code Two functions in utils.c, setting cgroup for comm and setting cgroup for pid, duplicate code for constructing the cgroup path. Extract it to a new helper function. - String and error handling fixes and cleanups There were several instances of unsafe string and error handling that could cause invalid memory access; fix them. Also, remove a few unused headers, update .gitignore, and deduplicate code. -----BEGIN PGP SIGNATURE----- iIoEABYKADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCaY3l2RQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qrWcAP4lsfIbNhfvmc9htDON/GlCu9FK8PTT hmAsO7MeHh29OwD+OS7RZzRza+3G+B4aTcqUXQVfAvx9qenS591LcktQSAo= =VUW2 -----END PGP SIGNATURE----- Merge tag 'trace-rtla-v7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull RTLA updates from Steven Rostedt: - Remove unused function declarations Some functions were removed in recent code consolidation 6.18, but their prototypes were not removed from headers. Remove them. - Set stop threshold after enabling instances Prefer recording samples without stopping on them on the start of tracing to stopping on samples that are never recorded. This fixes flakiness of some RTLA tests and unifies behavior of sample collection between tracefs mode and BPF mode. - Consolidate usage help message implementation RTLA tools (osnoise-top, osnoise-hist, timerlat-top, timerlat-hist) each implement usage help individually. Move common logic between them into a new function to reduce code duplication. - Add BPF actions feature Add option --bpf-action to attach a BPF program (passed as filename of its ELF representation) to be executed via BPF tail call at latency threshold. - Consolidate command line option parsing Each RTLA tool implements the parsing of command line options individually. Now that we have a common structure for parameters, unify the parsing of those options common among all four tools into one function. - De-duplicate cgroup common code Two functions in utils.c, setting cgroup for comm and setting cgroup for pid, duplicate code for constructing the cgroup path. Extract it to a new helper function. - String and error handling fixes and cleanups There were several instances of unsafe string and error handling that could cause invalid memory access; fix them. Also, remove a few unused headers, update .gitignore, and deduplicate code. * tag 'trace-rtla-v7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (30 commits) rtla: Fix parse_cpu_set() bug introduced by strtoi() rtla: Fix parse_cpu_set() return value documentation rtla: Ensure null termination after read operations in utils.c rtla: Make stop_tracing variable volatile rtla: Add generated output files to gitignore rtla: Fix NULL pointer dereference in actions_parse rtla: Remove unused headers rtla: Remove redundant memset after calloc rtla: Use standard exit codes for result enum rtla: Replace atoi() with a robust strtoi() rtla: Introduce for_each_action() helper tools/rtla: Deduplicate cgroup path opening code tools/rtla: Consolidate -H/--house-keeping option parsing tools/rtla: Consolidate -P/--priority option parsing tools/rtla: Consolidate -e/--event option parsing tools/rtla: Consolidate -d/--duration option parsing tools/rtla: Consolidate -D/--debug option parsing tools/rtla: Consolidate -C/--cgroup option parsing tools/rtla: Consolidate -c/--cpus option parsing tools/rtla: Add common_parse_options() ...
This commit is contained in:
commit
582a1ef360
26 changed files with 507 additions and 356 deletions
|
|
@ -64,4 +64,22 @@
|
|||
|
||||
Set timerlat to run without workload, waiting for the user to dispatch a per-cpu
|
||||
task that waits for a new period on the tracing/osnoise/per_cpu/cpu$ID/timerlat_fd.
|
||||
See linux/tools/rtla/sample/timerlat_load.py for an example of user-load code.
|
||||
See linux/tools/rtla/example/timerlat_load.py for an example of user-load code.
|
||||
|
||||
**--bpf-action** *bpf-program*
|
||||
|
||||
Loads a BPF program from an ELF file and executes it when a latency threshold is exceeded.
|
||||
|
||||
The BPF program must be a valid ELF file loadable with libbpf. The program must contain
|
||||
a function named ``action_handler``, stored in an ELF section with the ``tp_`` prefix.
|
||||
The prefix is used by libbpf to set BPF program type to BPF_PROG_TYPE_TRACEPOINT.
|
||||
|
||||
The program receives a ``struct trace_event_raw_timerlat_sample`` parameter
|
||||
containing timerlat sample data.
|
||||
|
||||
An example is provided in ``tools/tracing/rtla/example/timerlat_bpf_action.c``.
|
||||
This example demonstrates how to create a BPF program that prints latency information using
|
||||
bpf_trace_printk() when a threshold is exceeded.
|
||||
|
||||
**Note**: BPF actions require BPF support to be available. If BPF is not available
|
||||
or disabled, the tool falls back to tracefs mode and BPF actions are not supported.
|
||||
|
|
|
|||
4
tools/tracing/rtla/.gitignore
vendored
4
tools/tracing/rtla/.gitignore
vendored
|
|
@ -5,3 +5,7 @@ fixdep
|
|||
feature
|
||||
FEATURE-DUMP
|
||||
*.skel.h
|
||||
custom_filename.txt
|
||||
osnoise_irq_noise_hist.txt
|
||||
osnoise_trace.txt
|
||||
timerlat_trace.txt
|
||||
|
|
|
|||
|
|
@ -73,9 +73,21 @@ src/timerlat.bpf.o: src/timerlat.bpf.c
|
|||
|
||||
src/timerlat.skel.h: src/timerlat.bpf.o
|
||||
$(QUIET_GENSKEL)$(SYSTEM_BPFTOOL) gen skeleton $< > $@
|
||||
|
||||
example/timerlat_bpf_action.o: example/timerlat_bpf_action.c
|
||||
$(QUIET_CLANG)$(CLANG) -g -O2 -target bpf -c $(filter %.c,$^) -o $@
|
||||
|
||||
tests/bpf/bpf_action_map.o: tests/bpf/bpf_action_map.c
|
||||
$(QUIET_CLANG)$(CLANG) -g -O2 -target bpf -c $(filter %.c,$^) -o $@
|
||||
else
|
||||
src/timerlat.skel.h:
|
||||
$(Q)echo '/* BPF skeleton is disabled */' > src/timerlat.skel.h
|
||||
|
||||
example/timerlat_bpf_action.o: example/timerlat_bpf_action.c
|
||||
$(Q)echo "BPF skeleton support is disabled, skipping example/timerlat_bpf_action.o"
|
||||
|
||||
tests/bpf/bpf_action_map.o: tests/bpf/bpf_action_map.c
|
||||
$(Q)echo "BPF skeleton support is disabled, skipping tests/bpf/bpf_action_map.o"
|
||||
endif
|
||||
|
||||
$(RTLA): $(RTLA_IN)
|
||||
|
|
@ -96,7 +108,8 @@ 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/
|
||||
$(Q)rm -f src/timerlat.bpf.o src/timerlat.skel.h example/timerlat_bpf_action.o
|
||||
check: $(RTLA) tests/bpf/bpf_action_map.o
|
||||
RTLA=$(RTLA) BPFTOOL=$(SYSTEM_BPFTOOL) prove -o -f -v tests/
|
||||
examples: example/timerlat_bpf_action.o
|
||||
.PHONY: FORCE clean check
|
||||
|
|
|
|||
16
tools/tracing/rtla/example/timerlat_bpf_action.c
Normal file
16
tools/tracing/rtla/example/timerlat_bpf_action.c
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
|
||||
struct trace_event_raw_timerlat_sample {
|
||||
unsigned long long timer_latency;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
SEC("tp/timerlat_action")
|
||||
int action_handler(struct trace_event_raw_timerlat_sample *tp_args)
|
||||
{
|
||||
bpf_printk("Latency: %lld\n", tp_args->timer_latency);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -19,8 +19,6 @@ actions_init(struct actions *self)
|
|||
self->len = 0;
|
||||
self->continue_flag = false;
|
||||
|
||||
memset(&self->present, 0, sizeof(self->present));
|
||||
|
||||
/* This has to be set by the user */
|
||||
self->trace_output_inst = NULL;
|
||||
}
|
||||
|
|
@ -32,7 +30,9 @@ void
|
|||
actions_destroy(struct actions *self)
|
||||
{
|
||||
/* Free any action-specific data */
|
||||
for (struct action *action = self->list; action < self->list + self->len; action++) {
|
||||
struct action *action;
|
||||
|
||||
for_each_action(self, action) {
|
||||
if (action->type == ACTION_SHELL)
|
||||
free(action->command);
|
||||
if (action->type == ACTION_TRACE_OUTPUT)
|
||||
|
|
@ -141,6 +141,8 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn)
|
|||
|
||||
strcpy(trigger_c, trigger);
|
||||
token = strtok(trigger_c, ",");
|
||||
if (!token)
|
||||
return -1;
|
||||
|
||||
if (strcmp(token, "trace") == 0)
|
||||
type = ACTION_TRACE_OUTPUT;
|
||||
|
|
@ -179,12 +181,13 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn)
|
|||
/* Takes two arguments, num (signal) and pid */
|
||||
while (token != NULL) {
|
||||
if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) {
|
||||
signal = atoi(token + 4);
|
||||
if (strtoi(token + 4, &signal))
|
||||
return -1;
|
||||
} else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) {
|
||||
if (strncmp(token + 4, "parent", 7) == 0)
|
||||
pid = -1;
|
||||
else
|
||||
pid = atoi(token + 4);
|
||||
else if (strtoi(token + 4, &pid))
|
||||
return -1;
|
||||
} else {
|
||||
/* Invalid argument */
|
||||
return -1;
|
||||
|
|
@ -223,7 +226,7 @@ actions_perform(struct actions *self)
|
|||
int pid, retval;
|
||||
const struct action *action;
|
||||
|
||||
for (action = self->list; action < self->list + self->len; action++) {
|
||||
for_each_action(self, action) {
|
||||
switch (action->type) {
|
||||
case ACTION_TRACE_OUTPUT:
|
||||
retval = save_trace_to_file(self->trace_output_inst, action->trace_output);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,11 @@ struct actions {
|
|||
struct tracefs_instance *trace_output_inst;
|
||||
};
|
||||
|
||||
#define for_each_action(actions, action) \
|
||||
for ((action) = (actions)->list; \
|
||||
(action) < (actions)->list + (actions)->len; \
|
||||
(action)++)
|
||||
|
||||
void actions_init(struct actions *self);
|
||||
void actions_destroy(struct actions *self);
|
||||
int actions_add_trace_output(struct actions *self, const char *trace_output);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@
|
|||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include "common.h"
|
||||
|
||||
struct trace_instance *trace_inst;
|
||||
int stop_tracing;
|
||||
volatile int stop_tracing;
|
||||
|
||||
static void stop_trace(int sig)
|
||||
{
|
||||
|
|
@ -37,6 +39,84 @@ static void set_signals(struct common_params *params)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* common_parse_options - parse common command line options
|
||||
*
|
||||
* @argc: argument count
|
||||
* @argv: argument vector
|
||||
* @common: common parameters structure
|
||||
*
|
||||
* Parse command line options that are common to all rtla tools.
|
||||
*
|
||||
* Returns: non zero if a common option was parsed, or 0
|
||||
* if the option should be handled by tool-specific parsing.
|
||||
*/
|
||||
int common_parse_options(int argc, char **argv, struct common_params *common)
|
||||
{
|
||||
struct trace_events *tevent;
|
||||
int saved_state = optind;
|
||||
int c;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"cpus", required_argument, 0, 'c'},
|
||||
{"cgroup", optional_argument, 0, 'C'},
|
||||
{"debug", no_argument, 0, 'D'},
|
||||
{"duration", required_argument, 0, 'd'},
|
||||
{"event", required_argument, 0, 'e'},
|
||||
{"house-keeping", required_argument, 0, 'H'},
|
||||
{"priority", required_argument, 0, 'P'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
opterr = 0;
|
||||
c = getopt_long(argc, argv, "c:C::Dd:e:H:P:", long_options, NULL);
|
||||
opterr = 1;
|
||||
|
||||
switch (c) {
|
||||
case 'c':
|
||||
if (parse_cpu_set(optarg, &common->monitored_cpus))
|
||||
fatal("Invalid -c cpu list");
|
||||
common->cpus = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
common->cgroup = 1;
|
||||
common->cgroup_name = parse_optional_arg(argc, argv);
|
||||
break;
|
||||
case 'D':
|
||||
config_debug = 1;
|
||||
break;
|
||||
case 'd':
|
||||
common->duration = parse_seconds_duration(optarg);
|
||||
if (!common->duration)
|
||||
fatal("Invalid -d duration");
|
||||
break;
|
||||
case 'e':
|
||||
tevent = trace_event_alloc(optarg);
|
||||
if (!tevent)
|
||||
fatal("Error alloc trace event");
|
||||
|
||||
if (common->events)
|
||||
tevent->next = common->events;
|
||||
common->events = tevent;
|
||||
break;
|
||||
case 'H':
|
||||
common->hk_cpus = 1;
|
||||
if (parse_cpu_set(optarg, &common->hk_cpu_set))
|
||||
fatal("Error parsing house keeping CPUs");
|
||||
break;
|
||||
case 'P':
|
||||
if (parse_prio(optarg, &common->sched_param) == -1)
|
||||
fatal("Invalid -P priority");
|
||||
common->set_sched = 1;
|
||||
break;
|
||||
default:
|
||||
optind = saved_state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* common_apply_config - apply common configs to the initialized tool
|
||||
*/
|
||||
|
|
@ -348,3 +428,61 @@ int hist_main_loop(struct osnoise_tool *tool)
|
|||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int osn_set_stop(struct osnoise_tool *tool)
|
||||
{
|
||||
struct common_params *params = tool->params;
|
||||
int retval;
|
||||
|
||||
retval = osnoise_set_stop_us(tool->context, params->stop_us);
|
||||
if (retval) {
|
||||
err_msg("Failed to set stop us\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
|
||||
if (retval) {
|
||||
err_msg("Failed to set stop total us\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_msg_array(const char * const *msgs)
|
||||
{
|
||||
if (!msgs)
|
||||
return;
|
||||
|
||||
for (int i = 0; msgs[i]; i++)
|
||||
fprintf(stderr, "%s\n", msgs[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* common_usage - print complete usage information
|
||||
*/
|
||||
void common_usage(const char *tool, const char *mode,
|
||||
const char *desc, const char * const *start_msgs, const char * const *opt_msgs)
|
||||
{
|
||||
static const char * const common_options[] = {
|
||||
" -h/--help: print this menu",
|
||||
NULL
|
||||
};
|
||||
fprintf(stderr, "rtla %s", tool);
|
||||
if (strcmp(mode, ""))
|
||||
fprintf(stderr, " %s", mode);
|
||||
fprintf(stderr, ": %s (version %s)\n\n", desc, VERSION);
|
||||
fprintf(stderr, " usage: [rtla] %s ", tool);
|
||||
|
||||
if (strcmp(mode, "top") == 0)
|
||||
fprintf(stderr, "[top] [-h] ");
|
||||
else
|
||||
fprintf(stderr, "%s [-h] ", mode);
|
||||
|
||||
print_msg_array(start_msgs);
|
||||
fprintf(stderr, "\n");
|
||||
print_msg_array(common_options);
|
||||
print_msg_array(opt_msgs);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ struct osnoise_context {
|
|||
};
|
||||
|
||||
extern struct trace_instance *trace_inst;
|
||||
extern int stop_tracing;
|
||||
extern volatile int stop_tracing;
|
||||
|
||||
struct hist_params {
|
||||
char no_irq;
|
||||
|
|
@ -152,7 +152,15 @@ void osnoise_destroy_tool(struct osnoise_tool *top);
|
|||
struct osnoise_tool *osnoise_init_tool(char *tool_name);
|
||||
struct osnoise_tool *osnoise_init_trace_tool(const char *tracer);
|
||||
bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record);
|
||||
int osnoise_set_stop_us(struct osnoise_context *context, long long stop_us);
|
||||
int osnoise_set_stop_total_us(struct osnoise_context *context,
|
||||
long long stop_total_us);
|
||||
|
||||
int common_parse_options(int argc, char **argv, struct common_params *common);
|
||||
int common_apply_config(struct osnoise_tool *tool, struct common_params *params);
|
||||
int top_main_loop(struct osnoise_tool *tool);
|
||||
int hist_main_loop(struct osnoise_tool *tool);
|
||||
int osn_set_stop(struct osnoise_tool *tool);
|
||||
|
||||
void common_usage(const char *tool, const char *mode,
|
||||
const char *desc, const char * const *start_msgs, const char * const *opt_msgs);
|
||||
|
|
|
|||
|
|
@ -1128,18 +1128,6 @@ osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params)
|
|||
goto out_err;
|
||||
}
|
||||
|
||||
retval = osnoise_set_stop_us(tool->context, params->common.stop_us);
|
||||
if (retval) {
|
||||
err_msg("Failed to set stop us\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
retval = osnoise_set_stop_total_us(tool->context, params->common.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");
|
||||
|
|
@ -1184,9 +1172,12 @@ int osnoise_enable(struct osnoise_tool *tool)
|
|||
debug_msg("Error cleaning up the buffer");
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
retval = osn_set_stop(tool);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,12 +34,7 @@ int osnoise_set_runtime_period(struct osnoise_context *context,
|
|||
unsigned long long period);
|
||||
void osnoise_restore_runtime_period(struct osnoise_context *context);
|
||||
|
||||
int osnoise_set_stop_us(struct osnoise_context *context,
|
||||
long long stop_us);
|
||||
void osnoise_restore_stop_us(struct osnoise_context *context);
|
||||
|
||||
int osnoise_set_stop_total_us(struct osnoise_context *context,
|
||||
long long stop_total_us);
|
||||
void osnoise_restore_stop_total_us(struct osnoise_context *context);
|
||||
|
||||
int osnoise_set_timerlat_period_us(struct osnoise_context *context,
|
||||
|
|
@ -58,8 +53,6 @@ int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff);
|
|||
void osnoise_report_missed_events(struct osnoise_tool *tool);
|
||||
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);
|
||||
int osnoise_enable(struct osnoise_tool *tool);
|
||||
int osnoise_main(int argc, char **argv);
|
||||
int hwnoise_main(int argc, char **argv);
|
||||
|
|
@ -68,4 +61,3 @@ extern struct tool_ops timerlat_top_ops, timerlat_hist_ops;
|
|||
extern struct tool_ops osnoise_top_ops, osnoise_hist_ops;
|
||||
|
||||
int run_tool(struct tool_ops *ops, int argc, char *argv[]);
|
||||
int hist_main_loop(struct osnoise_tool *tool);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
|
|
@ -409,16 +408,15 @@ osnoise_print_stats(struct osnoise_tool *tool)
|
|||
*/
|
||||
static void osnoise_hist_usage(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
static const char * const msg[] = {
|
||||
"",
|
||||
" usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
|
||||
static const char * const msg_start[] = {
|
||||
"[-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
|
||||
" [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
|
||||
" [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\",
|
||||
" [--no-index] [--with-zeros] [-C [cgroup_name]] [--warm-up]",
|
||||
"",
|
||||
" -h/--help: print this menu",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char * const msg_opts[] = {
|
||||
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
|
||||
" -p/--period us: osnoise period in us",
|
||||
" -r/--runtime us: osnoise runtime in us",
|
||||
|
|
@ -453,13 +451,8 @@ static void osnoise_hist_usage(void)
|
|||
NULL,
|
||||
};
|
||||
|
||||
fprintf(stderr, "rtla osnoise hist: a per-cpu histogram of the OS noise (version %s)\n",
|
||||
VERSION);
|
||||
|
||||
for (i = 0; msg[i]; i++)
|
||||
fprintf(stderr, "%s\n", msg[i]);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
common_usage("osnoise", "hist", "a per-cpu histogram of the OS noise",
|
||||
msg_start, msg_opts);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -469,7 +462,6 @@ static struct common_params
|
|||
*osnoise_hist_parse_args(int argc, char *argv[])
|
||||
{
|
||||
struct osnoise_params *params;
|
||||
struct trace_events *tevent;
|
||||
int retval;
|
||||
int c;
|
||||
char *trace_output = NULL;
|
||||
|
|
@ -491,19 +483,12 @@ static struct common_params
|
|||
{"auto", required_argument, 0, 'a'},
|
||||
{"bucket-size", required_argument, 0, 'b'},
|
||||
{"entries", required_argument, 0, 'E'},
|
||||
{"cpus", required_argument, 0, 'c'},
|
||||
{"cgroup", optional_argument, 0, 'C'},
|
||||
{"debug", no_argument, 0, 'D'},
|
||||
{"duration", required_argument, 0, 'd'},
|
||||
{"house-keeping", required_argument, 0, 'H'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"period", required_argument, 0, 'p'},
|
||||
{"priority", required_argument, 0, 'P'},
|
||||
{"runtime", required_argument, 0, 'r'},
|
||||
{"stop", required_argument, 0, 's'},
|
||||
{"stop-total", required_argument, 0, 'S'},
|
||||
{"trace", optional_argument, 0, 't'},
|
||||
{"event", required_argument, 0, 'e'},
|
||||
{"threshold", required_argument, 0, 'T'},
|
||||
{"no-header", no_argument, 0, '0'},
|
||||
{"no-summary", no_argument, 0, '1'},
|
||||
|
|
@ -518,7 +503,10 @@ static struct common_params
|
|||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:p:P:r:s:S:t::T:01234:5:6:7:",
|
||||
if (common_parse_options(argc, argv, ¶ms->common))
|
||||
continue;
|
||||
|
||||
c = getopt_long(argc, argv, "a:b:E:hp:r:s:S:t::T:01234:5:6:7:",
|
||||
long_options, NULL);
|
||||
|
||||
/* detect the end of the options. */
|
||||
|
|
@ -544,34 +532,6 @@ static struct common_params
|
|||
params->common.hist.bucket_size >= 1000000)
|
||||
fatal("Bucket size needs to be > 0 and <= 1000000");
|
||||
break;
|
||||
case 'c':
|
||||
retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus);
|
||||
if (retval)
|
||||
fatal("Invalid -c cpu list");
|
||||
params->common.cpus = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
params->common.cgroup = 1;
|
||||
params->common.cgroup_name = parse_optional_arg(argc, argv);
|
||||
break;
|
||||
case 'D':
|
||||
config_debug = 1;
|
||||
break;
|
||||
case 'd':
|
||||
params->common.duration = parse_seconds_duration(optarg);
|
||||
if (!params->common.duration)
|
||||
fatal("Invalid -D duration");
|
||||
break;
|
||||
case 'e':
|
||||
tevent = trace_event_alloc(optarg);
|
||||
if (!tevent)
|
||||
fatal("Error alloc trace event");
|
||||
|
||||
if (params->common.events)
|
||||
tevent->next = params->common.events;
|
||||
|
||||
params->common.events = tevent;
|
||||
break;
|
||||
case 'E':
|
||||
params->common.hist.entries = get_llong_from_str(optarg);
|
||||
if (params->common.hist.entries < 10 ||
|
||||
|
|
@ -582,23 +542,11 @@ static struct common_params
|
|||
case '?':
|
||||
osnoise_hist_usage();
|
||||
break;
|
||||
case 'H':
|
||||
params->common.hk_cpus = 1;
|
||||
retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set);
|
||||
if (retval)
|
||||
fatal("Error parsing house keeping CPUs");
|
||||
break;
|
||||
case 'p':
|
||||
params->period = get_llong_from_str(optarg);
|
||||
if (params->period > 10000000)
|
||||
fatal("Period longer than 10 s");
|
||||
break;
|
||||
case 'P':
|
||||
retval = parse_prio(optarg, ¶ms->common.sched_param);
|
||||
if (retval == -1)
|
||||
fatal("Invalid -P priority");
|
||||
params->common.set_sched = 1;
|
||||
break;
|
||||
case 'r':
|
||||
params->runtime = get_llong_from_str(optarg);
|
||||
if (params->runtime < 100)
|
||||
|
|
|
|||
|
|
@ -257,14 +257,16 @@ osnoise_print_stats(struct osnoise_tool *top)
|
|||
*/
|
||||
static void osnoise_top_usage(struct osnoise_params *params)
|
||||
{
|
||||
int i;
|
||||
const char *tool, *mode, *desc;
|
||||
|
||||
static const char * const msg[] = {
|
||||
" [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
|
||||
static const char * const msg_start[] = {
|
||||
"[-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
|
||||
" [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
|
||||
" [-c cpu-list] [-H cpu-list] [-P priority] [-C [cgroup_name]] [--warm-up s]",
|
||||
"",
|
||||
" -h/--help: print this menu",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char * const msg_opts[] = {
|
||||
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
|
||||
" -p/--period us: osnoise period in us",
|
||||
" -r/--runtime us: osnoise runtime in us",
|
||||
|
|
@ -295,25 +297,16 @@ static void osnoise_top_usage(struct osnoise_params *params)
|
|||
};
|
||||
|
||||
if (params->mode == MODE_OSNOISE) {
|
||||
fprintf(stderr,
|
||||
"rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n",
|
||||
VERSION);
|
||||
|
||||
fprintf(stderr, " usage: rtla osnoise [top]");
|
||||
tool = "osnoise";
|
||||
mode = "top";
|
||||
desc = "a per-cpu summary of the OS noise";
|
||||
} else {
|
||||
tool = "hwnoise";
|
||||
mode = "";
|
||||
desc = "a summary of hardware-related noise";
|
||||
}
|
||||
|
||||
if (params->mode == MODE_HWNOISE) {
|
||||
fprintf(stderr,
|
||||
"rtla hwnoise: a summary of hardware-related noise (version %s)\n",
|
||||
VERSION);
|
||||
|
||||
fprintf(stderr, " usage: rtla hwnoise");
|
||||
}
|
||||
|
||||
for (i = 0; msg[i]; i++)
|
||||
fprintf(stderr, "%s\n", msg[i]);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
common_usage(tool, mode, desc, msg_start, msg_opts);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -322,7 +315,6 @@ static void osnoise_top_usage(struct osnoise_params *params)
|
|||
struct common_params *osnoise_top_parse_args(int argc, char **argv)
|
||||
{
|
||||
struct osnoise_params *params;
|
||||
struct trace_events *tevent;
|
||||
int retval;
|
||||
int c;
|
||||
char *trace_output = NULL;
|
||||
|
|
@ -346,15 +338,8 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv)
|
|||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"auto", required_argument, 0, 'a'},
|
||||
{"cpus", required_argument, 0, 'c'},
|
||||
{"cgroup", optional_argument, 0, 'C'},
|
||||
{"debug", no_argument, 0, 'D'},
|
||||
{"duration", required_argument, 0, 'd'},
|
||||
{"event", required_argument, 0, 'e'},
|
||||
{"house-keeping", required_argument, 0, 'H'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"period", required_argument, 0, 'p'},
|
||||
{"priority", required_argument, 0, 'P'},
|
||||
{"quiet", no_argument, 0, 'q'},
|
||||
{"runtime", required_argument, 0, 'r'},
|
||||
{"stop", required_argument, 0, 's'},
|
||||
|
|
@ -370,7 +355,10 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv)
|
|||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "a:c:C::d:De:hH:p:P:qr:s:S:t::T:0:1:2:3:",
|
||||
if (common_parse_options(argc, argv, ¶ms->common))
|
||||
continue;
|
||||
|
||||
c = getopt_long(argc, argv, "a:hp:qr:s:S:t::T:0:1:2:3:",
|
||||
long_options, NULL);
|
||||
|
||||
/* Detect the end of the options. */
|
||||
|
|
@ -389,56 +377,16 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv)
|
|||
if (!trace_output)
|
||||
trace_output = "osnoise_trace.txt";
|
||||
|
||||
break;
|
||||
case 'c':
|
||||
retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus);
|
||||
if (retval)
|
||||
fatal("Invalid -c cpu list");
|
||||
params->common.cpus = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
params->common.cgroup = 1;
|
||||
params->common.cgroup_name = parse_optional_arg(argc, argv);
|
||||
break;
|
||||
case 'D':
|
||||
config_debug = 1;
|
||||
break;
|
||||
case 'd':
|
||||
params->common.duration = parse_seconds_duration(optarg);
|
||||
if (!params->common.duration)
|
||||
fatal("Invalid -d duration");
|
||||
break;
|
||||
case 'e':
|
||||
tevent = trace_event_alloc(optarg);
|
||||
if (!tevent)
|
||||
fatal("Error alloc trace event");
|
||||
|
||||
if (params->common.events)
|
||||
tevent->next = params->common.events;
|
||||
params->common.events = tevent;
|
||||
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
osnoise_top_usage(params);
|
||||
break;
|
||||
case 'H':
|
||||
params->common.hk_cpus = 1;
|
||||
retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set);
|
||||
if (retval)
|
||||
fatal("Error parsing house keeping CPUs");
|
||||
break;
|
||||
case 'p':
|
||||
params->period = get_llong_from_str(optarg);
|
||||
if (params->period > 10000000)
|
||||
fatal("Period longer than 10 s");
|
||||
break;
|
||||
case 'P':
|
||||
retval = parse_prio(optarg, ¶ms->common.sched_param);
|
||||
if (retval == -1)
|
||||
fatal("Invalid -P priority");
|
||||
params->common.set_sched = 1;
|
||||
break;
|
||||
case 'q':
|
||||
params->common.quiet = 1;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,17 @@ struct {
|
|||
__uint(max_entries, 1);
|
||||
} signal_stop_tracing SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
|
||||
__uint(key_size, sizeof(unsigned int));
|
||||
__uint(max_entries, 1);
|
||||
__array(values, unsigned int (void *));
|
||||
} bpf_action SEC(".maps") = {
|
||||
.values = {
|
||||
[0] = 0
|
||||
},
|
||||
};
|
||||
|
||||
/* Params to be set by rtla */
|
||||
const volatile int bucket_size = 1;
|
||||
const volatile int output_divisor = 1000;
|
||||
|
|
@ -109,7 +120,7 @@ nosubprog void update_summary(void *map,
|
|||
map_set(map, SUMMARY_SUM, map_get(map, SUMMARY_SUM) + latency);
|
||||
}
|
||||
|
||||
nosubprog void set_stop_tracing(void)
|
||||
nosubprog void set_stop_tracing(struct trace_event_raw_timerlat_sample *tp_args)
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
|
|
@ -118,6 +129,12 @@ nosubprog void set_stop_tracing(void)
|
|||
|
||||
/* Signal to userspace */
|
||||
bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0);
|
||||
|
||||
/*
|
||||
* Call into BPF action program, if attached.
|
||||
* Otherwise, just silently fail.
|
||||
*/
|
||||
bpf_tail_call(tp_args, &bpf_action, 0);
|
||||
}
|
||||
|
||||
SEC("tp/osnoise/timerlat_sample")
|
||||
|
|
@ -138,19 +155,19 @@ int handle_timerlat_sample(struct trace_event_raw_timerlat_sample *tp_args)
|
|||
update_summary(&summary_irq, latency, bucket);
|
||||
|
||||
if (irq_threshold != 0 && latency_us >= irq_threshold)
|
||||
set_stop_tracing();
|
||||
set_stop_tracing(tp_args);
|
||||
} 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();
|
||||
set_stop_tracing(tp_args);
|
||||
} else {
|
||||
update_main_hist(&hist_user, bucket);
|
||||
update_summary(&summary_user, latency, bucket);
|
||||
|
||||
if (thread_threshold != 0 && latency_us >= thread_threshold)
|
||||
set_stop_tracing();
|
||||
set_stop_tracing(tp_args);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sched.h>
|
||||
|
|
@ -48,25 +47,17 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params)
|
|||
}
|
||||
}
|
||||
|
||||
if (params->mode != TRACING_MODE_BPF) {
|
||||
/*
|
||||
* In tracefs and mixed mode, timerlat tracer handles stopping
|
||||
* on threshold
|
||||
*/
|
||||
retval = osnoise_set_stop_us(tool->context, params->common.stop_us);
|
||||
if (retval) {
|
||||
err_msg("Failed to set stop us\n");
|
||||
/* Check if BPF action program is requested but BPF is not available */
|
||||
if (params->bpf_action_program) {
|
||||
if (params->mode == TRACING_MODE_TRACEFS) {
|
||||
err_msg("BPF actions are not supported in tracefs-only mode\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us);
|
||||
if (retval) {
|
||||
err_msg("Failed to set stop total us\n");
|
||||
if (timerlat_load_bpf_action_program(params->bpf_action_program))
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
retval = osnoise_set_timerlat_period_us(tool->context,
|
||||
params->timerlat_period_us ?
|
||||
params->timerlat_period_us :
|
||||
|
|
@ -184,6 +175,16 @@ int timerlat_enable(struct osnoise_tool *tool)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In tracefs and mixed mode, timerlat tracer handles stopping
|
||||
* on threshold
|
||||
*/
|
||||
if (params->mode != TRACING_MODE_BPF) {
|
||||
retval = osn_set_stop(tool);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ struct timerlat_params {
|
|||
int dump_tasks;
|
||||
int deepest_idle_state;
|
||||
enum timerlat_tracing_mode mode;
|
||||
const char *bpf_action_program;
|
||||
};
|
||||
|
||||
#define to_timerlat_params(ptr) container_of(ptr, struct timerlat_params, common)
|
||||
|
|
@ -36,4 +37,3 @@ int timerlat_main(int argc, char *argv[]);
|
|||
int timerlat_enable(struct osnoise_tool *tool);
|
||||
void timerlat_analyze(struct osnoise_tool *tool, bool stopped);
|
||||
void timerlat_free(struct osnoise_tool *tool);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
static struct timerlat_bpf *bpf;
|
||||
|
||||
/* BPF object and program for action program */
|
||||
static struct bpf_object *obj;
|
||||
static struct bpf_program *prog;
|
||||
|
||||
/*
|
||||
* timerlat_bpf_init - load and initialize BPF program to collect timerlat data
|
||||
*/
|
||||
|
|
@ -59,6 +63,19 @@ int timerlat_bpf_init(struct timerlat_params *params)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* timerlat_bpf_set_action - set action on threshold executed on BPF side
|
||||
*/
|
||||
static int timerlat_bpf_set_action(struct bpf_program *prog)
|
||||
{
|
||||
unsigned int key = 0, value = bpf_program__fd(prog);
|
||||
|
||||
return bpf_map__update_elem(bpf->maps.bpf_action,
|
||||
&key, sizeof(key),
|
||||
&value, sizeof(value),
|
||||
BPF_ANY);
|
||||
}
|
||||
|
||||
/*
|
||||
* timerlat_bpf_attach - attach BPF program to collect timerlat data
|
||||
*/
|
||||
|
|
@ -83,6 +100,11 @@ void timerlat_bpf_detach(void)
|
|||
void timerlat_bpf_destroy(void)
|
||||
{
|
||||
timerlat_bpf__destroy(bpf);
|
||||
bpf = NULL;
|
||||
if (obj)
|
||||
bpf_object__close(obj);
|
||||
obj = NULL;
|
||||
prog = NULL;
|
||||
}
|
||||
|
||||
static int handle_rb_event(void *ctx, void *data, size_t data_sz)
|
||||
|
|
@ -177,4 +199,48 @@ int timerlat_bpf_get_summary_value(enum summary_field key,
|
|||
bpf->maps.summary_user,
|
||||
key, value_irq, value_thread, value_user, cpus);
|
||||
}
|
||||
|
||||
/*
|
||||
* timerlat_load_bpf_action_program - load and register a BPF action program
|
||||
*/
|
||||
int timerlat_load_bpf_action_program(const char *program_path)
|
||||
{
|
||||
int err;
|
||||
|
||||
obj = bpf_object__open_file(program_path, NULL);
|
||||
if (!obj) {
|
||||
err_msg("Failed to open BPF action program: %s\n", program_path);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (err) {
|
||||
err_msg("Failed to load BPF action program: %s\n", program_path);
|
||||
goto out_obj_err;
|
||||
}
|
||||
|
||||
prog = bpf_object__find_program_by_name(obj, "action_handler");
|
||||
if (!prog) {
|
||||
err_msg("BPF action program must have 'action_handler' function: %s\n",
|
||||
program_path);
|
||||
goto out_obj_err;
|
||||
}
|
||||
|
||||
err = timerlat_bpf_set_action(prog);
|
||||
if (err) {
|
||||
err_msg("Failed to register BPF action program: %s\n", program_path);
|
||||
goto out_prog_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_prog_err:
|
||||
prog = NULL;
|
||||
out_obj_err:
|
||||
bpf_object__close(obj);
|
||||
obj = NULL;
|
||||
out_err:
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* HAVE_BPF_SKEL */
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ enum summary_field {
|
|||
};
|
||||
|
||||
#ifndef __bpf__
|
||||
#include <bpf/libbpf.h>
|
||||
#ifdef HAVE_BPF_SKEL
|
||||
int timerlat_bpf_init(struct timerlat_params *params);
|
||||
int timerlat_bpf_attach(void);
|
||||
|
|
@ -29,7 +30,7 @@ int timerlat_bpf_get_summary_value(enum summary_field key,
|
|||
long long *value_thread,
|
||||
long long *value_user,
|
||||
int cpus);
|
||||
|
||||
int timerlat_load_bpf_action_program(const char *program_path);
|
||||
static inline int have_libbpf_support(void) { return 1; }
|
||||
#else
|
||||
static inline int timerlat_bpf_init(struct timerlat_params *params)
|
||||
|
|
@ -57,6 +58,10 @@ static inline int timerlat_bpf_get_summary_value(enum summary_field key,
|
|||
{
|
||||
return -1;
|
||||
}
|
||||
static inline int timerlat_load_bpf_action_program(const char *program_path)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static inline int have_libbpf_support(void) { return 0; }
|
||||
#endif /* HAVE_BPF_SKEL */
|
||||
#endif /* __bpf__ */
|
||||
|
|
|
|||
|
|
@ -696,17 +696,16 @@ timerlat_print_stats(struct osnoise_tool *tool)
|
|||
*/
|
||||
static void timerlat_hist_usage(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
char *msg[] = {
|
||||
"",
|
||||
" usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\",
|
||||
static const char * const msg_start[] = {
|
||||
"[-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\",
|
||||
" [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",
|
||||
" [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",
|
||||
" [--no-index] [--with-zeros] [--dma-latency us] [-C [cgroup_name]] [--no-aa] [--dump-task] [-u|-k]",
|
||||
" [--warm-up s] [--deepest-idle-state n]",
|
||||
"",
|
||||
" -h/--help: print this menu",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char * const msg_opts[] = {
|
||||
" -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
|
||||
" -p/--period us: timerlat period in us",
|
||||
" -i/--irq us: stop trace if the irq latency is higher than the argument in us",
|
||||
|
|
@ -747,16 +746,12 @@ static void timerlat_hist_usage(void)
|
|||
" --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency",
|
||||
" --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed",
|
||||
" --on-end <action>: define action to be executed at measurement end, multiple are allowed",
|
||||
" --bpf-action <program>: load and execute BPF program when latency threshold is exceeded",
|
||||
NULL,
|
||||
};
|
||||
|
||||
fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n",
|
||||
VERSION);
|
||||
|
||||
for (i = 0; msg[i]; i++)
|
||||
fprintf(stderr, "%s\n", msg[i]);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
common_usage("timerlat", "hist", "a per-cpu histogram of the timer latency",
|
||||
msg_start, msg_opts);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -766,7 +761,6 @@ static struct common_params
|
|||
*timerlat_hist_parse_args(int argc, char *argv[])
|
||||
{
|
||||
struct timerlat_params *params;
|
||||
struct trace_events *tevent;
|
||||
int auto_thresh;
|
||||
int retval;
|
||||
int c;
|
||||
|
|
@ -796,25 +790,18 @@ static struct common_params
|
|||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"auto", required_argument, 0, 'a'},
|
||||
{"cpus", required_argument, 0, 'c'},
|
||||
{"cgroup", optional_argument, 0, 'C'},
|
||||
{"bucket-size", required_argument, 0, 'b'},
|
||||
{"debug", no_argument, 0, 'D'},
|
||||
{"entries", required_argument, 0, 'E'},
|
||||
{"duration", required_argument, 0, 'd'},
|
||||
{"house-keeping", required_argument, 0, 'H'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"irq", required_argument, 0, 'i'},
|
||||
{"nano", no_argument, 0, 'n'},
|
||||
{"period", required_argument, 0, 'p'},
|
||||
{"priority", required_argument, 0, 'P'},
|
||||
{"stack", required_argument, 0, 's'},
|
||||
{"thread", required_argument, 0, 'T'},
|
||||
{"trace", optional_argument, 0, 't'},
|
||||
{"user-threads", no_argument, 0, 'u'},
|
||||
{"kernel-threads", no_argument, 0, 'k'},
|
||||
{"user-load", no_argument, 0, 'U'},
|
||||
{"event", required_argument, 0, 'e'},
|
||||
{"no-irq", no_argument, 0, '0'},
|
||||
{"no-thread", no_argument, 0, '1'},
|
||||
{"no-header", no_argument, 0, '2'},
|
||||
|
|
@ -831,10 +818,14 @@ static struct common_params
|
|||
{"deepest-idle-state", required_argument, 0, '\4'},
|
||||
{"on-threshold", required_argument, 0, '\5'},
|
||||
{"on-end", required_argument, 0, '\6'},
|
||||
{"bpf-action", required_argument, 0, '\7'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:i:knp:P:s:t::T:uU0123456:7:8:9\1\2:\3:",
|
||||
if (common_parse_options(argc, argv, ¶ms->common))
|
||||
continue;
|
||||
|
||||
c = getopt_long(argc, argv, "a:b:E:hi:knp:s:t::T:uU0123456:7:8:9\1\2:\3:",
|
||||
long_options, NULL);
|
||||
|
||||
/* detect the end of the options. */
|
||||
|
|
@ -856,16 +847,6 @@ static struct common_params
|
|||
if (!trace_output)
|
||||
trace_output = "timerlat_trace.txt";
|
||||
|
||||
break;
|
||||
case 'c':
|
||||
retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus);
|
||||
if (retval)
|
||||
fatal("Invalid -c cpu list");
|
||||
params->common.cpus = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
params->common.cgroup = 1;
|
||||
params->common.cgroup_name = parse_optional_arg(argc, argv);
|
||||
break;
|
||||
case 'b':
|
||||
params->common.hist.bucket_size = get_llong_from_str(optarg);
|
||||
|
|
@ -873,24 +854,6 @@ static struct common_params
|
|||
params->common.hist.bucket_size >= 1000000)
|
||||
fatal("Bucket size needs to be > 0 and <= 1000000");
|
||||
break;
|
||||
case 'D':
|
||||
config_debug = 1;
|
||||
break;
|
||||
case 'd':
|
||||
params->common.duration = parse_seconds_duration(optarg);
|
||||
if (!params->common.duration)
|
||||
fatal("Invalid -D duration");
|
||||
break;
|
||||
case 'e':
|
||||
tevent = trace_event_alloc(optarg);
|
||||
if (!tevent)
|
||||
fatal("Error alloc trace event");
|
||||
|
||||
if (params->common.events)
|
||||
tevent->next = params->common.events;
|
||||
|
||||
params->common.events = tevent;
|
||||
break;
|
||||
case 'E':
|
||||
params->common.hist.entries = get_llong_from_str(optarg);
|
||||
if (params->common.hist.entries < 10 ||
|
||||
|
|
@ -901,12 +864,6 @@ static struct common_params
|
|||
case '?':
|
||||
timerlat_hist_usage();
|
||||
break;
|
||||
case 'H':
|
||||
params->common.hk_cpus = 1;
|
||||
retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set);
|
||||
if (retval)
|
||||
fatal("Error parsing house keeping CPUs");
|
||||
break;
|
||||
case 'i':
|
||||
params->common.stop_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
|
|
@ -921,12 +878,6 @@ static struct common_params
|
|||
if (params->timerlat_period_us > 1000000)
|
||||
fatal("Period longer than 1 s");
|
||||
break;
|
||||
case 'P':
|
||||
retval = parse_prio(optarg, ¶ms->common.sched_param);
|
||||
if (retval == -1)
|
||||
fatal("Invalid -P priority");
|
||||
params->common.set_sched = 1;
|
||||
break;
|
||||
case 's':
|
||||
params->print_stack = get_llong_from_str(optarg);
|
||||
break;
|
||||
|
|
@ -1012,6 +963,9 @@ static struct common_params
|
|||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '\7':
|
||||
params->bpf_action_program = optarg;
|
||||
break;
|
||||
default:
|
||||
fatal("Invalid option");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
|
@ -476,15 +475,14 @@ timerlat_print_stats(struct osnoise_tool *top)
|
|||
*/
|
||||
static void timerlat_top_usage(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
static const char *const msg[] = {
|
||||
"",
|
||||
" usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\",
|
||||
static const char *const msg_start[] = {
|
||||
"[-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\",
|
||||
" [[-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",
|
||||
" [-P priority] [--dma-latency us] [--aa-only us] [-C [cgroup_name]] [-u|-k] [--warm-up s] [--deepest-idle-state n]",
|
||||
"",
|
||||
" -h/--help: print this menu",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *const msg_opts[] = {
|
||||
" -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
|
||||
" --aa-only us: stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)",
|
||||
" -p/--period us: timerlat period in us",
|
||||
|
|
@ -519,16 +517,12 @@ static void timerlat_top_usage(void)
|
|||
" --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency",
|
||||
" --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed",
|
||||
" --on-end: define action to be executed at measurement end, multiple are allowed",
|
||||
" --bpf-action <program>: load and execute BPF program when latency threshold is exceeded",
|
||||
NULL,
|
||||
};
|
||||
|
||||
fprintf(stderr, "rtla timerlat top: a per-cpu summary of the timer latency (version %s)\n",
|
||||
VERSION);
|
||||
|
||||
for (i = 0; msg[i]; i++)
|
||||
fprintf(stderr, "%s\n", msg[i]);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
common_usage("timerlat", "top", "a per-cpu summary of the timer latency",
|
||||
msg_start, msg_opts);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -538,7 +532,6 @@ static struct common_params
|
|||
*timerlat_top_parse_args(int argc, char **argv)
|
||||
{
|
||||
struct timerlat_params *params;
|
||||
struct trace_events *tevent;
|
||||
long long auto_thresh;
|
||||
int retval;
|
||||
int c;
|
||||
|
|
@ -566,17 +559,10 @@ static struct common_params
|
|||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"auto", required_argument, 0, 'a'},
|
||||
{"cpus", required_argument, 0, 'c'},
|
||||
{"cgroup", optional_argument, 0, 'C'},
|
||||
{"debug", no_argument, 0, 'D'},
|
||||
{"duration", required_argument, 0, 'd'},
|
||||
{"event", required_argument, 0, 'e'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"house-keeping", required_argument, 0, 'H'},
|
||||
{"irq", required_argument, 0, 'i'},
|
||||
{"nano", no_argument, 0, 'n'},
|
||||
{"period", required_argument, 0, 'p'},
|
||||
{"priority", required_argument, 0, 'P'},
|
||||
{"quiet", no_argument, 0, 'q'},
|
||||
{"stack", required_argument, 0, 's'},
|
||||
{"thread", required_argument, 0, 'T'},
|
||||
|
|
@ -595,10 +581,14 @@ static struct common_params
|
|||
{"deepest-idle-state", required_argument, 0, '8'},
|
||||
{"on-threshold", required_argument, 0, '9'},
|
||||
{"on-end", required_argument, 0, '\1'},
|
||||
{"bpf-action", required_argument, 0, '\2'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:knp:P:qs:t::T:uU0:1:2:345:6:7:",
|
||||
if (common_parse_options(argc, argv, ¶ms->common))
|
||||
continue;
|
||||
|
||||
c = getopt_long(argc, argv, "a:hi:knp:qs:t::T:uU0:1:2:345:6:7:",
|
||||
long_options, NULL);
|
||||
|
||||
/* detect the end of the options. */
|
||||
|
|
@ -635,43 +625,10 @@ static struct common_params
|
|||
/* set aa_only to avoid parsing the trace */
|
||||
params->common.aa_only = 1;
|
||||
break;
|
||||
case 'c':
|
||||
retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus);
|
||||
if (retval)
|
||||
fatal("Invalid -c cpu list");
|
||||
params->common.cpus = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
params->common.cgroup = 1;
|
||||
params->common.cgroup_name = optarg;
|
||||
break;
|
||||
case 'D':
|
||||
config_debug = 1;
|
||||
break;
|
||||
case 'd':
|
||||
params->common.duration = parse_seconds_duration(optarg);
|
||||
if (!params->common.duration)
|
||||
fatal("Invalid -d duration");
|
||||
break;
|
||||
case 'e':
|
||||
tevent = trace_event_alloc(optarg);
|
||||
if (!tevent)
|
||||
fatal("Error alloc trace event");
|
||||
|
||||
if (params->common.events)
|
||||
tevent->next = params->common.events;
|
||||
params->common.events = tevent;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
timerlat_top_usage();
|
||||
break;
|
||||
case 'H':
|
||||
params->common.hk_cpus = 1;
|
||||
retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set);
|
||||
if (retval)
|
||||
fatal("Error parsing house keeping CPUs");
|
||||
break;
|
||||
case 'i':
|
||||
params->common.stop_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
|
|
@ -686,12 +643,6 @@ static struct common_params
|
|||
if (params->timerlat_period_us > 1000000)
|
||||
fatal("Period longer than 1 s");
|
||||
break;
|
||||
case 'P':
|
||||
retval = parse_prio(optarg, ¶ms->common.sched_param);
|
||||
if (retval == -1)
|
||||
fatal("Invalid -P priority");
|
||||
params->common.set_sched = 1;
|
||||
break;
|
||||
case 'q':
|
||||
params->common.quiet = 1;
|
||||
break;
|
||||
|
|
@ -762,6 +713,9 @@ static struct common_params
|
|||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '\2':
|
||||
params->bpf_action_program = optarg;
|
||||
break;
|
||||
default:
|
||||
fatal("Invalid option");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <sys/sendfile.h>
|
||||
#include <tracefs.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ void get_duration(time_t start_time, char *output, int output_size)
|
|||
* Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set
|
||||
* filling cpu_set_t argument.
|
||||
*
|
||||
* Returns 1 on success, 0 otherwise.
|
||||
* Returns 0 on success, 1 otherwise.
|
||||
*/
|
||||
int parse_cpu_set(char *cpu_list, cpu_set_t *set)
|
||||
{
|
||||
|
|
@ -314,6 +315,7 @@ static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_e
|
|||
if (retval <= 0)
|
||||
return 0;
|
||||
|
||||
buffer[MAX_PATH-1] = '\0';
|
||||
retval = strncmp(comm_prefix, buffer, strlen(comm_prefix));
|
||||
if (retval)
|
||||
return 0;
|
||||
|
|
@ -337,6 +339,7 @@ int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr)
|
|||
struct dirent *proc_entry;
|
||||
DIR *procfs;
|
||||
int retval;
|
||||
int pid;
|
||||
|
||||
if (strlen(comm_prefix) >= MAX_PATH) {
|
||||
err_msg("Command prefix is too long: %d < strlen(%s)\n",
|
||||
|
|
@ -356,8 +359,12 @@ int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr)
|
|||
if (!retval)
|
||||
continue;
|
||||
|
||||
if (strtoi(proc_entry->d_name, &pid)) {
|
||||
err_msg("'%s' is not a valid pid", proc_entry->d_name);
|
||||
goto out_err;
|
||||
}
|
||||
/* procfs_is_workload_pid confirmed it is a pid */
|
||||
retval = __set_sched_attr(atoi(proc_entry->d_name), attr);
|
||||
retval = __set_sched_attr(pid, attr);
|
||||
if (retval) {
|
||||
err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name);
|
||||
goto out_err;
|
||||
|
|
@ -742,6 +749,7 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg)
|
|||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
retval = read(fd, path, MAX_PATH);
|
||||
|
||||
close(fd);
|
||||
|
|
@ -749,6 +757,7 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg)
|
|||
if (retval <= 0)
|
||||
return 0;
|
||||
|
||||
path[MAX_PATH-1] = '\0';
|
||||
start = path;
|
||||
|
||||
start = strstr(start, ":");
|
||||
|
|
@ -784,27 +793,27 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg)
|
|||
}
|
||||
|
||||
/*
|
||||
* set_comm_cgroup - Set cgroup to pid_t pid
|
||||
* open_cgroup_procs - Open the cgroup.procs file for the given cgroup
|
||||
*
|
||||
* If cgroup argument is not NULL, the threads will move to the given cgroup.
|
||||
* Otherwise, the cgroup of the calling, i.e., rtla, thread will be used.
|
||||
* If cgroup argument is not NULL, the cgroup.procs file for that cgroup
|
||||
* will be opened. Otherwise, the cgroup of the calling, i.e., rtla, thread
|
||||
* will be used.
|
||||
*
|
||||
* Supports cgroup v2.
|
||||
*
|
||||
* Returns 1 on success, 0 otherwise.
|
||||
* Returns the file descriptor on success, -1 otherwise.
|
||||
*/
|
||||
int set_pid_cgroup(pid_t pid, const char *cgroup)
|
||||
static int open_cgroup_procs(const char *cgroup)
|
||||
{
|
||||
char cgroup_path[MAX_PATH - strlen("/cgroup.procs")];
|
||||
char cgroup_procs[MAX_PATH];
|
||||
char pid_str[24];
|
||||
int retval;
|
||||
int cg_fd;
|
||||
|
||||
retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path));
|
||||
if (!retval) {
|
||||
err_msg("Did not find cgroupv2 mount point\n");
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!cgroup) {
|
||||
|
|
@ -812,7 +821,7 @@ int set_pid_cgroup(pid_t pid, const char *cgroup)
|
|||
sizeof(cgroup_path) - strlen(cgroup_path));
|
||||
if (!retval) {
|
||||
err_msg("Did not find self cgroup\n");
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
snprintf(&cgroup_path[strlen(cgroup_path)],
|
||||
|
|
@ -824,6 +833,29 @@ int set_pid_cgroup(pid_t pid, const char *cgroup)
|
|||
debug_msg("Using cgroup path at: %s\n", cgroup_procs);
|
||||
|
||||
cg_fd = open(cgroup_procs, O_RDWR);
|
||||
if (cg_fd < 0)
|
||||
return -1;
|
||||
|
||||
return cg_fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_pid_cgroup - Set cgroup to pid_t pid
|
||||
*
|
||||
* If cgroup argument is not NULL, the threads will move to the given cgroup.
|
||||
* Otherwise, the cgroup of the calling, i.e., rtla, thread will be used.
|
||||
*
|
||||
* Supports cgroup v2.
|
||||
*
|
||||
* Returns 1 on success, 0 otherwise.
|
||||
*/
|
||||
int set_pid_cgroup(pid_t pid, const char *cgroup)
|
||||
{
|
||||
char pid_str[24];
|
||||
int retval;
|
||||
int cg_fd;
|
||||
|
||||
cg_fd = open_cgroup_procs(cgroup);
|
||||
if (cg_fd < 0)
|
||||
return 0;
|
||||
|
||||
|
|
@ -853,8 +885,6 @@ int set_pid_cgroup(pid_t pid, const char *cgroup)
|
|||
*/
|
||||
int set_comm_cgroup(const char *comm_prefix, const char *cgroup)
|
||||
{
|
||||
char cgroup_path[MAX_PATH - strlen("/cgroup.procs")];
|
||||
char cgroup_procs[MAX_PATH];
|
||||
struct dirent *proc_entry;
|
||||
DIR *procfs;
|
||||
int retval;
|
||||
|
|
@ -866,29 +896,7 @@ int set_comm_cgroup(const char *comm_prefix, const char *cgroup)
|
|||
return 0;
|
||||
}
|
||||
|
||||
retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path));
|
||||
if (!retval) {
|
||||
err_msg("Did not find cgroupv2 mount point\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cgroup) {
|
||||
retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)],
|
||||
sizeof(cgroup_path) - strlen(cgroup_path));
|
||||
if (!retval) {
|
||||
err_msg("Did not find self cgroup\n");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
snprintf(&cgroup_path[strlen(cgroup_path)],
|
||||
sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup);
|
||||
}
|
||||
|
||||
snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path);
|
||||
|
||||
debug_msg("Using cgroup path at: %s\n", cgroup_procs);
|
||||
|
||||
cg_fd = open(cgroup_procs, O_RDWR);
|
||||
cg_fd = open_cgroup_procs(cgroup);
|
||||
if (cg_fd < 0)
|
||||
return 0;
|
||||
|
||||
|
|
@ -1000,3 +1008,25 @@ char *parse_optional_arg(int argc, char **argv)
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* strtoi - convert string to integer with error checking
|
||||
*
|
||||
* Returns 0 on success, -1 if conversion fails or result is out of int range.
|
||||
*/
|
||||
int strtoi(const char *s, int *res)
|
||||
{
|
||||
char *end_ptr;
|
||||
long lres;
|
||||
|
||||
if (!*s)
|
||||
return -1;
|
||||
|
||||
errno = 0;
|
||||
lres = strtol(s, &end_ptr, 0);
|
||||
if (errno || *end_ptr || lres > INT_MAX || lres < INT_MIN)
|
||||
return -1;
|
||||
|
||||
*res = (int) lres;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* '18446744073709551615\0'
|
||||
|
|
@ -24,7 +26,6 @@ void fatal(const char *fmt, ...);
|
|||
long parse_seconds_duration(char *val);
|
||||
void get_duration(time_t start_time, char *output, int output_size);
|
||||
|
||||
int parse_cpu_list(char *cpu_list, char **monitored_cpus);
|
||||
char *parse_optional_arg(int argc, char **argv);
|
||||
long long get_llong_from_str(char *start);
|
||||
|
||||
|
|
@ -82,12 +83,13 @@ static inline int set_deepest_cpu_idle_state(unsigned int cpu, unsigned int stat
|
|||
static inline int have_libcpupower_support(void) { return 0; }
|
||||
#endif /* HAVE_LIBCPUPOWER_SUPPORT */
|
||||
int auto_house_keeping(cpu_set_t *monitored_cpus);
|
||||
__attribute__((__warn_unused_result__)) int strtoi(const char *s, int *res);
|
||||
|
||||
#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 */
|
||||
PASSED = EXIT_SUCCESS,
|
||||
ERROR = EXIT_FAILURE,
|
||||
FAILED, /* test hit the stop tracing condition */
|
||||
};
|
||||
|
|
|
|||
25
tools/tracing/rtla/tests/bpf/bpf_action_map.c
Normal file
25
tools/tracing/rtla/tests/bpf/bpf_action_map.c
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, unsigned int);
|
||||
__type(value, unsigned long long);
|
||||
} rtla_test_map SEC(".maps");
|
||||
|
||||
struct trace_event_raw_timerlat_sample;
|
||||
|
||||
SEC("tp/timerlat_action")
|
||||
int action_handler(struct trace_event_raw_timerlat_sample *tp_args)
|
||||
{
|
||||
unsigned int key = 0;
|
||||
unsigned long long value = 42;
|
||||
|
||||
bpf_map_update_elem(&rtla_test_map, &key, &value, BPF_ANY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -105,7 +105,6 @@ check_with_osnoise_options() {
|
|||
[ "$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
|
||||
|
|
|
|||
|
|
@ -67,6 +67,21 @@ check "hist with trace output at end" \
|
|||
"timerlat hist -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$"
|
||||
check "top with trace output at end" \
|
||||
"timerlat top -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$"
|
||||
|
||||
# BPF action program tests
|
||||
if [ "$option" -eq 0 ]
|
||||
then
|
||||
# Test BPF action program properly in BPF mode
|
||||
[ -z "$BPFTOOL" ] && BPFTOOL=bpftool
|
||||
check "hist with BPF action program (BPF mode)" \
|
||||
"timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o --on-threshold shell,command='$BPFTOOL map dump name rtla_test_map'" \
|
||||
2 '"value": 42'
|
||||
else
|
||||
# Test BPF action program failure in non-BPF mode
|
||||
check "hist with BPF action program (non-BPF mode)" \
|
||||
"timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o" \
|
||||
1 "BPF actions are not supported in tracefs-only mode"
|
||||
fi
|
||||
done
|
||||
|
||||
test_end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue