Skip to content

Commit 36492b7

Browse files
committed
Merge tag 'tracepoints-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull unused tracepoints update from Steven Rostedt: "Detect unused tracepoints. If a tracepoint is defined but never used (TRACE_EVENT() created but no trace_<tracepoint>() called), it can take up to or more than 5K of memory each. This can add up as there are around a hundred unused tracepoints with various configs. That is 500K of wasted memory. Add a make build parameter of "UT=1" to have the build warn if an unused tracepoint is detected in the build. This allows detection of unused tracepoints to be upstream so that outreachy and the mentoring project can have new developers look for fixing them, without having these warnings suddenly show up when someone upgrades their kernel. When all known unused tracepoints are removed, then the "UT=1" build parameter can be removed and unused tracepoints will always warn. This will catch new unused tracepoints after the current ones have been removed. Summary: - Separate out elf functions from sorttable.c Move out the ELF parsing functions from sorttable.c so that the tracing tooling can use it. - Add a tracepoint verifier tool to the build process If "UT=1" is added to the kernel command line, any unused tracepoints will trigger a warning at build time. - Do not warn about unused tracepoints for tracepoints that are exported There are sever cases where a tracepoint is created by the kernel and used by modules. Since there's no easy way to detect if these are truly unused since the users are in modules, if a tracepoint is exported, assume it will eventually be used by a module. Note, there's not many exported tracepoints so this should not be a problem to ignore them. - Have building of modules also detect unused tracepoints Do not only check the main vmlinux for unused tracepoints, also check modules. If a module is defining a tracepoint it should be using it. - Add the tracepoint-update program to the ignore file The new tracepoint-update program needs to be ignored by git" * tag 'tracepoints-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: scripts: add tracepoint-update to the list of ignores files tracing: Add warnings for unused tracepoints for modules tracing: Allow tracepoint-update.c to work with modules tracepoint: Do not warn for unused event that is exported tracing: Add a tracepoint verification check at build time sorttable: Move ELF parsing into scripts/elf-parse.[ch]
2 parents 5779de8 + b21f90e commit 36492b7

11 files changed

Lines changed: 852 additions & 443 deletions

File tree

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,25 @@ ifdef CONFIG_FUNCTION_TRACER
810810
CC_FLAGS_FTRACE := -pg
811811
endif
812812

813+
ifdef CONFIG_TRACEPOINTS
814+
# To check for unused tracepoints (tracepoints that are defined but never
815+
# called), run with:
816+
#
817+
# make UT=1
818+
#
819+
# Each unused tracepoints can take up to 5KB of memory in the running kernel.
820+
# It is best to remove any that are not used.
821+
#
822+
# This command line option will be removed when all current unused
823+
# tracepoints are removed.
824+
825+
ifeq ("$(origin UT)", "command line")
826+
WARN_ON_UNUSED_TRACEPOINTS := $(UT)
827+
endif
828+
endif # CONFIG_TRACEPOINTS
829+
830+
export WARN_ON_UNUSED_TRACEPOINTS
831+
813832
include $(srctree)/arch/$(SRCARCH)/Makefile
814833

815834
ifdef need-config
@@ -1787,6 +1806,8 @@ help:
17871806
@echo ' c: extra checks in the configuration stage (Kconfig)'
17881807
@echo ' e: warnings are being treated as errors'
17891808
@echo ' Multiple levels can be combined with W=12 or W=123'
1809+
@echo ' make UT=1 [targets] Warn if a tracepoint is defined but not used.'
1810+
@echo ' [ This will be removed when all current unused tracepoints are eliminated. ]'
17901811
@$(if $(dtstree), \
17911812
echo ' make CHECK_DTBS=1 [targets] Check all generated dtb files against schema'; \
17921813
echo ' This can be applied both to "dtbs" and to individual "foo.dtb" targets' ; \

include/asm-generic/vmlinux.lds.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,7 @@
10651065
*(.no_trim_symbol) \
10661066
/* ld.bfd warns about .gnu.version* even when not emitted */ \
10671067
*(.gnu.version*) \
1068+
*(__tracepoint_check) \
10681069

10691070
#define DISCARDS \
10701071
/DISCARD/ : { \

include/linux/tracepoint.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,15 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
221221
__do_trace_##name(args); \
222222
}
223223

224+
/*
225+
* When a tracepoint is used, it's name is added to the __tracepoint_check
226+
* section. This section is only used at build time to make sure all
227+
* defined tracepoints are used. It is discarded after the build.
228+
*/
229+
# define TRACEPOINT_CHECK(name) \
230+
static const char __used __section("__tracepoint_check") \
231+
__trace_check_##name[] = #name;
232+
224233
/*
225234
* Make sure the alignment of the structure in the __tracepoints section will
226235
* not add unwanted padding between the beginning of the section and the
@@ -270,6 +279,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
270279
__DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) \
271280
static inline void __do_trace_##name(proto) \
272281
{ \
282+
TRACEPOINT_CHECK(name) \
273283
if (cond) { \
274284
guard(preempt_notrace)(); \
275285
__DO_TRACE_CALL(name, TP_ARGS(args)); \
@@ -289,6 +299,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
289299
__DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) \
290300
static inline void __do_trace_##name(proto) \
291301
{ \
302+
TRACEPOINT_CHECK(name) \
292303
guard(rcu_tasks_trace)(); \
293304
__DO_TRACE_CALL(name, TP_ARGS(args)); \
294305
} \
@@ -371,10 +382,12 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
371382
__DEFINE_TRACE_EXT(_name, NULL, PARAMS(_proto), PARAMS(_args));
372383

373384
#define EXPORT_TRACEPOINT_SYMBOL_GPL(name) \
385+
TRACEPOINT_CHECK(name) \
374386
EXPORT_SYMBOL_GPL(__tracepoint_##name); \
375387
EXPORT_SYMBOL_GPL(__traceiter_##name); \
376388
EXPORT_STATIC_CALL_GPL(tp_func_##name)
377389
#define EXPORT_TRACEPOINT_SYMBOL(name) \
390+
TRACEPOINT_CHECK(name) \
378391
EXPORT_SYMBOL(__tracepoint_##name); \
379392
EXPORT_SYMBOL(__traceiter_##name); \
380393
EXPORT_STATIC_CALL(tp_func_##name)

scripts/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
/sign-file
1212
/sorttable
1313
/target.json
14+
/tracepoint-update
1415
/unifdef

scripts/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT) += sign-file
1111
hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
1212
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_builder
1313
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_gen
14+
hostprogs-always-$(CONFIG_TRACEPOINTS) += tracepoint-update
15+
16+
sorttable-objs := sorttable.o elf-parse.o
17+
tracepoint-update-objs := tracepoint-update.o elf-parse.o
1418

1519
ifneq ($(or $(CONFIG_X86_64),$(CONFIG_X86_32)),)
1620
always-$(CONFIG_RUST) += target.json
@@ -25,6 +29,8 @@ generate_rust_target-rust := y
2529
rustdoc_test_builder-rust := y
2630
rustdoc_test_gen-rust := y
2731

32+
HOSTCFLAGS_tracepoint-update.o = -I$(srctree)/tools/include
33+
HOSTCFLAGS_elf-parse.o = -I$(srctree)/tools/include
2834
HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
2935
HOSTLDLIBS_sorttable = -lpthread
3036
HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include

scripts/Makefile.modfinal

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ ccflags-remove-y := $(CC_FLAGS_CFI)
2828
.module-common.o: $(srctree)/scripts/module-common.c FORCE
2929
$(call if_changed_rule,cc_o_c)
3030

31+
ifneq ($(WARN_ON_UNUSED_TRACEPOINTS),)
32+
cmd_check_tracepoint = $(objtree)/scripts/tracepoint-update --module $<;
33+
endif
34+
3135
quiet_cmd_ld_ko_o = LD [M] $@
3236
cmd_ld_ko_o = \
3337
$(LD) -r $(KBUILD_LDFLAGS) \
@@ -57,6 +61,7 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \
5761
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
5862
+$(if $(newer-prereqs),$(call cmd,btf_ko))
5963
endif
64+
+$(call cmd,check_tracepoint)
6065

6166
targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
6267

scripts/elf-parse.c

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#include <sys/types.h>
2+
#include <sys/mman.h>
3+
#include <sys/stat.h>
4+
#include <fcntl.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <stdbool.h>
8+
#include <string.h>
9+
#include <unistd.h>
10+
#include <errno.h>
11+
12+
#include "elf-parse.h"
13+
14+
struct elf_funcs elf_parser;
15+
16+
/*
17+
* Get the whole file as a programming convenience in order to avoid
18+
* malloc+lseek+read+free of many pieces. If successful, then mmap
19+
* avoids copying unused pieces; else just read the whole file.
20+
* Open for both read and write.
21+
*/
22+
static void *map_file(char const *fname, size_t *size)
23+
{
24+
int fd;
25+
struct stat sb;
26+
void *addr = NULL;
27+
28+
fd = open(fname, O_RDWR);
29+
if (fd < 0) {
30+
perror(fname);
31+
return NULL;
32+
}
33+
if (fstat(fd, &sb) < 0) {
34+
perror(fname);
35+
goto out;
36+
}
37+
if (!S_ISREG(sb.st_mode)) {
38+
fprintf(stderr, "not a regular file: %s\n", fname);
39+
goto out;
40+
}
41+
42+
addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
43+
if (addr == MAP_FAILED) {
44+
fprintf(stderr, "Could not mmap file: %s\n", fname);
45+
goto out;
46+
}
47+
48+
*size = sb.st_size;
49+
50+
out:
51+
close(fd);
52+
return addr;
53+
}
54+
55+
static int elf_parse(const char *fname, void *addr, uint32_t types)
56+
{
57+
Elf_Ehdr *ehdr = addr;
58+
uint16_t type;
59+
60+
switch (ehdr->e32.e_ident[EI_DATA]) {
61+
case ELFDATA2LSB:
62+
elf_parser.r = rle;
63+
elf_parser.r2 = r2le;
64+
elf_parser.r8 = r8le;
65+
elf_parser.w = wle;
66+
elf_parser.w8 = w8le;
67+
break;
68+
case ELFDATA2MSB:
69+
elf_parser.r = rbe;
70+
elf_parser.r2 = r2be;
71+
elf_parser.r8 = r8be;
72+
elf_parser.w = wbe;
73+
elf_parser.w8 = w8be;
74+
break;
75+
default:
76+
fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
77+
ehdr->e32.e_ident[EI_DATA], fname);
78+
return -1;
79+
}
80+
81+
if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
82+
ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
83+
fprintf(stderr, "unrecognized ELF file %s\n", fname);
84+
return -1;
85+
}
86+
87+
type = elf_parser.r2(&ehdr->e32.e_type);
88+
if (!((1 << type) & types)) {
89+
fprintf(stderr, "Invalid ELF type file %s\n", fname);
90+
return -1;
91+
}
92+
93+
switch (ehdr->e32.e_ident[EI_CLASS]) {
94+
case ELFCLASS32: {
95+
elf_parser.ehdr_shoff = ehdr32_shoff;
96+
elf_parser.ehdr_shentsize = ehdr32_shentsize;
97+
elf_parser.ehdr_shstrndx = ehdr32_shstrndx;
98+
elf_parser.ehdr_shnum = ehdr32_shnum;
99+
elf_parser.shdr_addr = shdr32_addr;
100+
elf_parser.shdr_offset = shdr32_offset;
101+
elf_parser.shdr_link = shdr32_link;
102+
elf_parser.shdr_size = shdr32_size;
103+
elf_parser.shdr_name = shdr32_name;
104+
elf_parser.shdr_type = shdr32_type;
105+
elf_parser.shdr_entsize = shdr32_entsize;
106+
elf_parser.sym_type = sym32_type;
107+
elf_parser.sym_name = sym32_name;
108+
elf_parser.sym_value = sym32_value;
109+
elf_parser.sym_shndx = sym32_shndx;
110+
elf_parser.rela_offset = rela32_offset;
111+
elf_parser.rela_info = rela32_info;
112+
elf_parser.rela_addend = rela32_addend;
113+
elf_parser.rela_write_addend = rela32_write_addend;
114+
115+
if (elf_parser.r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||
116+
elf_parser.r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {
117+
fprintf(stderr,
118+
"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
119+
return -1;
120+
}
121+
122+
}
123+
break;
124+
case ELFCLASS64: {
125+
elf_parser.ehdr_shoff = ehdr64_shoff;
126+
elf_parser.ehdr_shentsize = ehdr64_shentsize;
127+
elf_parser.ehdr_shstrndx = ehdr64_shstrndx;
128+
elf_parser.ehdr_shnum = ehdr64_shnum;
129+
elf_parser.shdr_addr = shdr64_addr;
130+
elf_parser.shdr_offset = shdr64_offset;
131+
elf_parser.shdr_link = shdr64_link;
132+
elf_parser.shdr_size = shdr64_size;
133+
elf_parser.shdr_name = shdr64_name;
134+
elf_parser.shdr_type = shdr64_type;
135+
elf_parser.shdr_entsize = shdr64_entsize;
136+
elf_parser.sym_type = sym64_type;
137+
elf_parser.sym_name = sym64_name;
138+
elf_parser.sym_value = sym64_value;
139+
elf_parser.sym_shndx = sym64_shndx;
140+
elf_parser.rela_offset = rela64_offset;
141+
elf_parser.rela_info = rela64_info;
142+
elf_parser.rela_addend = rela64_addend;
143+
elf_parser.rela_write_addend = rela64_write_addend;
144+
145+
if (elf_parser.r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||
146+
elf_parser.r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {
147+
fprintf(stderr,
148+
"unrecognized ET_EXEC/ET_DYN file: %s\n",
149+
fname);
150+
return -1;
151+
}
152+
153+
}
154+
break;
155+
default:
156+
fprintf(stderr, "unrecognized ELF class %d %s\n",
157+
ehdr->e32.e_ident[EI_CLASS], fname);
158+
return -1;
159+
}
160+
return 0;
161+
}
162+
163+
int elf_map_machine(void *addr)
164+
{
165+
Elf_Ehdr *ehdr = addr;
166+
167+
return elf_parser.r2(&ehdr->e32.e_machine);
168+
}
169+
170+
int elf_map_long_size(void *addr)
171+
{
172+
Elf_Ehdr *ehdr = addr;
173+
174+
return ehdr->e32.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
175+
}
176+
177+
void *elf_map(char const *fname, size_t *size, uint32_t types)
178+
{
179+
void *addr;
180+
int ret;
181+
182+
addr = map_file(fname, size);
183+
if (!addr)
184+
return NULL;
185+
186+
ret = elf_parse(fname, addr, types);
187+
if (ret < 0) {
188+
elf_unmap(addr, *size);
189+
return NULL;
190+
}
191+
192+
return addr;
193+
}
194+
195+
void elf_unmap(void *addr, size_t size)
196+
{
197+
munmap(addr, size);
198+
}

0 commit comments

Comments
 (0)