Skip to content

Commit 9e1e9d6

Browse files
committed
Merge tag 'trace-rtla-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull RTLA updates from Steven Rostedt: - Simplify option parsing Auto-generate getopt_long() optstring for short options from long options array, avoiding the need to specify it manually and reducing the surface for mistakes. - Add unit tests Implement unit tests (make unit-tests) using libcheck, next to existing runtime tests (make check). Currently, three functions from utils.c are tested. - Add --stack-format option In addition to stopping stack pointer decoding (with -s/--stack option) on first unresolvable pointer, allow also skipping unresolvable pointers and displaying everything, configurable with a new option. - Unify number of CPUs into one global variable Use one global variable, nr_cpus, to store the number of CPUs instead of retrieving it and passing it at multiple places. - Fix behavior in various corner cases Make RTLA behave correctly in several corner cases: memory allocation failure, invalid value read from kernel side, thread creation failure, malformed time value input, and read/write failure or interruption by signal. - Improve string handling Simplify several places in the code that handle strings, including parsing of action arguments. A few new helper functions and variables are added for that purpose. - Get rid of magic numbers Few places handling paths use a magic number of 1024. Replace it with MAX_PATH and ARRAY_SIZE() macro. - Unify threshold handling Code that handles response to latency threshold is duplicated between tools, which has led to bugs in the past. Unify it into a new helper as much as possible. - Fix segfault on SIGINT during cleanup The SIGINT handler touches dynamically allocated memory. Detach it before freeing it during cleanup to prevent segmentation fault and discarding of output buffers. Also, properly document SIGINT handling while at it. * tag 'trace-rtla-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (28 commits) Documentation/rtla: Document SIGINT behavior rtla: Fix segfault on multiple SIGINTs rtla/utils: Fix loop condition in PID validation rtla/utils: Fix resource leak in set_comm_sched_attr() rtla/trace: Fix I/O handling in save_trace_to_file() rtla/trace: Fix write loop in trace_event_save_hist() rtla/timerlat: Simplify RTLA_NO_BPF environment variable check rtla: Use str_has_prefix() for option prefix check rtla: Enforce exact match for time unit suffixes rtla: Use str_has_prefix() for prefix checks rtla: Add str_has_prefix() helper function rtla: Handle pthread_create() failure properly rtla/timerlat: Add bounds check for softirq vector rtla: Simplify code by caching string lengths rtla: Replace magic number with MAX_PATH rtla: Introduce common_threshold_handler() helper rtla/actions: Simplify argument parsing rtla: Use strdup() to simplify code rtla: Exit on memory allocation failures during initialization tools/rtla: Remove unneeded nr_cpus from for_each_monitored_cpu ...
2 parents fdbfee9 + 8237499 commit 9e1e9d6

33 files changed

Lines changed: 769 additions & 402 deletions

Documentation/tools/rtla/common_appendix.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
.. SPDX-License-Identifier: GPL-2.0
22

3+
SIGINT BEHAVIOR
4+
===============
5+
6+
On the first SIGINT, RTLA exits after collecting all outstanding samples up to
7+
the point of receiving the signal.
8+
9+
When receiving more than one SIGINT, RTLA discards any outstanding samples, and
10+
exits while displaying only samples that have already been processed.
11+
12+
If SIGINT is received during RTLA cleanup, RTLA exits immediately via
13+
the default signal handler.
14+
15+
Note: For the purpose of SIGINT behavior, the expiry of duration specified via
16+
the -d/--duration option is treated as equivalent to receiving a SIGINT. For
17+
example, a SIGINT received after duration expired but samples have not been
18+
processed yet will drop any outstanding samples.
19+
20+
Also note that when using the timerlat tool in BPF mode, samples are processed
21+
in-kernel; RTLA only copies them out to display them to the user. A second
22+
SIGINT does not affect in-kernel sample aggregation.
23+
324
EXIT STATUS
425
===========
526

Documentation/tools/rtla/common_timerlat_options.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,15 @@
8383

8484
**Note**: BPF actions require BPF support to be available. If BPF is not available
8585
or disabled, the tool falls back to tracefs mode and BPF actions are not supported.
86+
87+
**--stack-format** *format*
88+
89+
Adjust the format of the stack trace printed during auto-analysis.
90+
91+
The supported values for *format* are:
92+
93+
* **truncate** Print the stack trace up to the first unknown address (default).
94+
* **skip** Skip unknown addresses.
95+
* **full** Print the entire stack trace, including unknown addresses.
96+
97+
For unknown addresses, the raw pointer is printed.

tools/build/Makefile.feature

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ FEATURE_TESTS_EXTRA := \
115115
hello \
116116
libbabeltrace \
117117
libcapstone \
118+
libcheck \
118119
libbfd-liberty \
119120
libbfd-liberty-z \
120121
libopencsd \
@@ -176,6 +177,8 @@ ifneq ($(PKG_CONFIG),)
176177
$(foreach package,$(FEATURE_PKG_CONFIG),$(call feature_pkg_config,$(package)))
177178
endif
178179

180+
FEATURE_CHECK_LDFLAGS-libcheck = -lcheck
181+
179182
# Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
180183
# If in the future we need per-feature checks/flags for features not
181184
# mentioned in this list we need to refactor this ;-).

tools/build/feature/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ FILES= \
5050
test-timerfd.bin \
5151
test-libbabeltrace.bin \
5252
test-libcapstone.bin \
53+
test-libcheck.bin \
5354
test-compile-32.bin \
5455
test-compile-x32.bin \
5556
test-zlib.bin \
@@ -307,6 +308,9 @@ $(OUTPUT)test-libbabeltrace.bin:
307308
$(OUTPUT)test-libcapstone.bin:
308309
$(BUILD) # -lcapstone provided by $(FEATURE_CHECK_LDFLAGS-libcapstone)
309310

311+
$(OUTPUT)test-libcheck.bin:
312+
$(BUILD) # -lcheck is provided by $(FEATURE_CHECK_LDFLAGS-libcheck)
313+
310314
$(OUTPUT)test-compile-32.bin:
311315
$(CC) -m32 -Wall -Werror -o $@ test-compile.c
312316

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <check.h>
3+
4+
int main(void)
5+
{
6+
Suite *s = suite_create("test");
7+
return s == 0;
8+
}

tools/tracing/rtla/Build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
rtla-y += src/
2+
unit_tests-y += tests/unit/

tools/tracing/rtla/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ DOCSRC := ../../../Documentation/tools/rtla/
3333
FEATURE_TESTS := libtraceevent
3434
FEATURE_TESTS += libtracefs
3535
FEATURE_TESTS += libcpupower
36+
FEATURE_TESTS += libcheck
3637
FEATURE_TESTS += libbpf
3738
FEATURE_TESTS += clang-bpf-co-re
3839
FEATURE_TESTS += bpftool-skeletons
3940
FEATURE_DISPLAY := libtraceevent
4041
FEATURE_DISPLAY += libtracefs
4142
FEATURE_DISPLAY += libcpupower
43+
FEATURE_DISPLAY += libcheck
4244
FEATURE_DISPLAY += libbpf
4345
FEATURE_DISPLAY += clang-bpf-co-re
4446
FEATURE_DISPLAY += bpftool-skeletons
@@ -47,6 +49,7 @@ all: $(RTLA)
4749

4850
include $(srctree)/tools/build/Makefile.include
4951
include Makefile.rtla
52+
include tests/unit/Makefile.unit
5053

5154
# check for dependencies only on required targets
5255
NON_CONFIG_TARGETS := clean install tarball doc doc_clean doc_install
@@ -109,6 +112,8 @@ clean: doc_clean fixdep-clean
109112
$(Q)rm -f rtla rtla-static fixdep FEATURE-DUMP rtla-*
110113
$(Q)rm -rf feature
111114
$(Q)rm -f src/timerlat.bpf.o src/timerlat.skel.h example/timerlat_bpf_action.o
115+
$(Q)rm -f $(UNIT_TESTS)
116+
112117
check: $(RTLA) tests/bpf/bpf_action_map.o
113118
RTLA=$(RTLA) BPFTOOL=$(SYSTEM_BPFTOOL) prove -o -f -v tests/
114119
examples: example/timerlat_bpf_action.o

tools/tracing/rtla/Makefile.config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ else
6161
$(info Please install libcpupower-dev/kernel-tools-libs-devel)
6262
endif
6363

64+
$(call feature_check,libcheck)
65+
ifeq ($(feature-libcheck), 1)
66+
$(call detected,CONFIG_LIBCHECK)
67+
else
68+
$(info libcheck is missing, building without unit tests support.)
69+
$(info Please install check-devel/check)
70+
endif
71+
6472
ifndef BUILD_BPF_SKEL
6573
# BPF skeletons are used to implement improved sample collection, enable
6674
# them by default.

tools/tracing/rtla/README.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ RTLA depends on the following libraries and tools:
1212
- libtracefs
1313
- libtraceevent
1414
- libcpupower (optional, for --deepest-idle-state)
15+
- libcheck (optional, for unit tests)
1516

1617
For BPF sample collection support, the following extra dependencies are
1718
required:

tools/tracing/rtla/src/actions.c

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void
1515
actions_init(struct actions *self)
1616
{
1717
self->size = action_default_size;
18-
self->list = calloc(self->size, sizeof(struct action));
18+
self->list = calloc_fatal(self->size, sizeof(struct action));
1919
self->len = 0;
2020
self->continue_flag = false;
2121

@@ -50,8 +50,10 @@ static struct action *
5050
actions_new(struct actions *self)
5151
{
5252
if (self->len >= self->size) {
53-
self->size *= 2;
54-
self->list = realloc(self->list, self->size * sizeof(struct action));
53+
const size_t new_size = self->size * 2;
54+
55+
self->list = reallocarray_fatal(self->list, new_size, sizeof(struct action));
56+
self->size = new_size;
5557
}
5658

5759
return &self->list[self->len++];
@@ -60,25 +62,20 @@ actions_new(struct actions *self)
6062
/*
6163
* actions_add_trace_output - add an action to output trace
6264
*/
63-
int
65+
void
6466
actions_add_trace_output(struct actions *self, const char *trace_output)
6567
{
6668
struct action *action = actions_new(self);
6769

6870
self->present[ACTION_TRACE_OUTPUT] = true;
6971
action->type = ACTION_TRACE_OUTPUT;
70-
action->trace_output = calloc(strlen(trace_output) + 1, sizeof(char));
71-
if (!action->trace_output)
72-
return -1;
73-
strcpy(action->trace_output, trace_output);
74-
75-
return 0;
72+
action->trace_output = strdup_fatal(trace_output);
7673
}
7774

7875
/*
7976
* actions_add_trace_output - add an action to send signal to a process
8077
*/
81-
int
78+
void
8279
actions_add_signal(struct actions *self, int signal, int pid)
8380
{
8481
struct action *action = actions_new(self);
@@ -87,42 +84,56 @@ actions_add_signal(struct actions *self, int signal, int pid)
8784
action->type = ACTION_SIGNAL;
8885
action->signal = signal;
8986
action->pid = pid;
90-
91-
return 0;
9287
}
9388

9489
/*
9590
* actions_add_shell - add an action to execute a shell command
9691
*/
97-
int
92+
void
9893
actions_add_shell(struct actions *self, const char *command)
9994
{
10095
struct action *action = actions_new(self);
10196

10297
self->present[ACTION_SHELL] = true;
10398
action->type = ACTION_SHELL;
104-
action->command = calloc(strlen(command) + 1, sizeof(char));
105-
if (!action->command)
106-
return -1;
107-
strcpy(action->command, command);
108-
109-
return 0;
99+
action->command = strdup_fatal(command);
110100
}
111101

112102
/*
113103
* actions_add_continue - add an action to resume measurement
114104
*/
115-
int
105+
void
116106
actions_add_continue(struct actions *self)
117107
{
118108
struct action *action = actions_new(self);
119109

120110
self->present[ACTION_CONTINUE] = true;
121111
action->type = ACTION_CONTINUE;
112+
}
122113

123-
return 0;
114+
static inline const char *__extract_arg(const char *token, const char *opt, size_t opt_len)
115+
{
116+
const size_t tok_len = strlen(token);
117+
118+
if (tok_len <= opt_len)
119+
return NULL;
120+
121+
if (strncmp(token, opt, opt_len))
122+
return NULL;
123+
124+
return token + opt_len;
124125
}
125126

127+
/*
128+
* extract_arg - extract argument value from option token
129+
* @token: option token (e.g., "file=trace.txt")
130+
* @opt: option name to match (e.g., "file")
131+
*
132+
* Returns pointer to argument value after "=" if token matches "opt=",
133+
* otherwise returns NULL.
134+
*/
135+
#define extract_arg(token, opt) __extract_arg(token, opt "=", STRING_LENGTH(opt "="))
136+
126137
/*
127138
* actions_parse - add an action based on text specification
128139
*/
@@ -132,6 +143,7 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn)
132143
enum action_type type = ACTION_NONE;
133144
const char *token;
134145
char trigger_c[strlen(trigger) + 1];
146+
const char *arg_value;
135147

136148
/* For ACTION_SIGNAL */
137149
int signal = 0, pid = 0;
@@ -164,33 +176,36 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn)
164176
if (token == NULL)
165177
trace_output = tracefn;
166178
else {
167-
if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) {
168-
trace_output = token + 5;
169-
} else {
179+
trace_output = extract_arg(token, "file");
180+
if (!trace_output)
170181
/* Invalid argument */
171182
return -1;
172-
}
173183

174184
token = strtok(NULL, ",");
175185
if (token != NULL)
176186
/* Only one argument allowed */
177187
return -1;
178188
}
179-
return actions_add_trace_output(self, trace_output);
189+
actions_add_trace_output(self, trace_output);
190+
break;
180191
case ACTION_SIGNAL:
181192
/* Takes two arguments, num (signal) and pid */
182193
while (token != NULL) {
183-
if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) {
184-
if (strtoi(token + 4, &signal))
185-
return -1;
186-
} else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) {
187-
if (strncmp(token + 4, "parent", 7) == 0)
188-
pid = -1;
189-
else if (strtoi(token + 4, &pid))
194+
arg_value = extract_arg(token, "num");
195+
if (arg_value) {
196+
if (strtoi(arg_value, &signal))
190197
return -1;
191198
} else {
192-
/* Invalid argument */
193-
return -1;
199+
arg_value = extract_arg(token, "pid");
200+
if (arg_value) {
201+
if (strncmp_static(arg_value, "parent") == 0)
202+
pid = -1;
203+
else if (strtoi(arg_value, &pid))
204+
return -1;
205+
} else {
206+
/* Invalid argument */
207+
return -1;
208+
}
194209
}
195210

196211
token = strtok(NULL, ",");
@@ -200,21 +215,27 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn)
200215
/* Missing argument */
201216
return -1;
202217

203-
return actions_add_signal(self, signal, pid);
218+
actions_add_signal(self, signal, pid);
219+
break;
204220
case ACTION_SHELL:
205221
if (token == NULL)
206222
return -1;
207-
if (strlen(token) > 8 && strncmp(token, "command=", 8) == 0)
208-
return actions_add_shell(self, token + 8);
209-
return -1;
223+
arg_value = extract_arg(token, "command");
224+
if (!arg_value)
225+
return -1;
226+
actions_add_shell(self, arg_value);
227+
break;
210228
case ACTION_CONTINUE:
211229
/* Takes no argument */
212230
if (token != NULL)
213231
return -1;
214-
return actions_add_continue(self);
232+
actions_add_continue(self);
233+
break;
215234
default:
216235
return -1;
217236
}
237+
238+
return 0;
218239
}
219240

220241
/*

0 commit comments

Comments
 (0)