xref: /linux/scripts/tracepoint-update.c (revision 36492b7141b9abc967e92c991af32c670351dc16)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <getopt.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stdbool.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <pthread.h>
14 
15 #include "elf-parse.h"
16 
17 static Elf_Shdr *check_data_sec;
18 static Elf_Shdr *tracepoint_data_sec;
19 
get_index(void * start,int entsize,int index)20 static inline void *get_index(void *start, int entsize, int index)
21 {
22 	return start + (entsize * index);
23 }
24 
compare_strings(const void * a,const void * b)25 static int compare_strings(const void *a, const void *b)
26 {
27 	const char *av = *(const char **)a;
28 	const char *bv = *(const char **)b;
29 
30 	return strcmp(av, bv);
31 }
32 
33 struct elf_tracepoint {
34 	Elf_Ehdr *ehdr;
35 	const char **array;
36 	int count;
37 };
38 
39 #define REALLOC_SIZE (1 << 10)
40 #define REALLOC_MASK (REALLOC_SIZE - 1)
41 
add_string(const char * str,const char *** vals,int * count)42 static int add_string(const char *str, const char ***vals, int *count)
43 {
44 	const char **array = *vals;
45 
46 	if (!(*count & REALLOC_MASK)) {
47 		int size = (*count) + REALLOC_SIZE;
48 
49 		array = realloc(array, sizeof(char *) * size);
50 		if (!array) {
51 			fprintf(stderr, "Failed memory allocation\n");
52 			return -1;
53 		}
54 		*vals = array;
55 	}
56 
57 	array[(*count)++] = str;
58 	return 0;
59 }
60 
61 /**
62  * for_each_shdr_str - iterator that reads strings that are in an ELF section.
63  * @len: "int" to hold the length of the current string
64  * @ehdr: A pointer to the ehdr of the ELF file
65  * @sec: The section that has the strings to iterate on
66  *
67  * This is a for loop that iterates over all the nul terminated strings
68  * that are in a given ELF section. The variable "str" will hold
69  * the current string for each iteration and the passed in @len will
70  * contain the strlen() of that string.
71  */
72 #define for_each_shdr_str(len, ehdr, sec)				\
73 	for (const char *str = (void *)(ehdr) + shdr_offset(sec),	\
74 			*end = str + shdr_size(sec);			\
75 	     len = strlen(str), str < end;				\
76 	     str += (len) + 1)
77 
78 
make_trace_array(struct elf_tracepoint * etrace)79 static void make_trace_array(struct elf_tracepoint *etrace)
80 {
81 	Elf_Ehdr *ehdr = etrace->ehdr;
82 	const char **vals = NULL;
83 	int count = 0;
84 	int len;
85 
86 	etrace->array = NULL;
87 
88 	/*
89 	 * The __tracepoint_check section is filled with strings of the
90 	 * names of tracepoints (in tracepoint_strings). Create an array
91 	 * that points to each string and then sort the array.
92 	 */
93 	for_each_shdr_str(len, ehdr, check_data_sec) {
94 		if (!len)
95 			continue;
96 		if (add_string(str, &vals, &count) < 0)
97 			return;
98 	}
99 
100 	/* If CONFIG_TRACEPOINT_VERIFY_USED is not set, there's nothing to do */
101 	if (!count)
102 		return;
103 
104 	qsort(vals, count, sizeof(char *), compare_strings);
105 
106 	etrace->array = vals;
107 	etrace->count = count;
108 }
109 
find_event(const char * str,void * array,size_t size)110 static int find_event(const char *str, void *array, size_t size)
111 {
112 	return bsearch(&str, array, size, sizeof(char *), compare_strings) != NULL;
113 }
114 
check_tracepoints(struct elf_tracepoint * etrace,const char * fname)115 static void check_tracepoints(struct elf_tracepoint *etrace, const char *fname)
116 {
117 	Elf_Ehdr *ehdr = etrace->ehdr;
118 	int len;
119 
120 	if (!etrace->array)
121 		return;
122 
123 	/*
124 	 * The __tracepoints_strings section holds all the names of the
125 	 * defined tracepoints. If any of them are not in the
126 	 * __tracepoint_check_section it means they are not used.
127 	 */
128 	for_each_shdr_str(len, ehdr, tracepoint_data_sec) {
129 		if (!len)
130 			continue;
131 		if (!find_event(str, etrace->array, etrace->count)) {
132 			fprintf(stderr, "warning: tracepoint '%s' is unused", str);
133 			if (fname)
134 				fprintf(stderr, " in module %s\n", fname);
135 			else
136 				fprintf(stderr, "\n");
137 		}
138 	}
139 
140 	free(etrace->array);
141 }
142 
tracepoint_check(struct elf_tracepoint * etrace,const char * fname)143 static void *tracepoint_check(struct elf_tracepoint *etrace, const char *fname)
144 {
145 	make_trace_array(etrace);
146 	check_tracepoints(etrace, fname);
147 
148 	return NULL;
149 }
150 
process_tracepoints(bool mod,void * addr,const char * fname)151 static int process_tracepoints(bool mod, void *addr, const char *fname)
152 {
153 	struct elf_tracepoint etrace = {0};
154 	Elf_Ehdr *ehdr = addr;
155 	Elf_Shdr *shdr_start;
156 	Elf_Shdr *string_sec;
157 	const char *secstrings;
158 	unsigned int shnum;
159 	unsigned int shstrndx;
160 	int shentsize;
161 	int idx;
162 	int done = 2;
163 
164 	shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
165 	shentsize = ehdr_shentsize(ehdr);
166 
167 	shstrndx = ehdr_shstrndx(ehdr);
168 	if (shstrndx == SHN_XINDEX)
169 		shstrndx = shdr_link(shdr_start);
170 	string_sec = get_index(shdr_start, shentsize, shstrndx);
171 	secstrings = (const char *)ehdr + shdr_offset(string_sec);
172 
173 	shnum = ehdr_shnum(ehdr);
174 	if (shnum == SHN_UNDEF)
175 		shnum = shdr_size(shdr_start);
176 
177 	for (int i = 0; done && i < shnum; i++) {
178 		Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
179 
180 		idx = shdr_name(shdr);
181 
182 		/* locate the __tracepoint_check in vmlinux */
183 		if (!strcmp(secstrings + idx, "__tracepoint_check")) {
184 			check_data_sec = shdr;
185 			done--;
186 		}
187 
188 		/* locate the __tracepoints_ptrs section in vmlinux */
189 		if (!strcmp(secstrings + idx, "__tracepoints_strings")) {
190 			tracepoint_data_sec = shdr;
191 			done--;
192 		}
193 	}
194 
195 	/*
196 	 * Modules may not have either section. But if it has one section,
197 	 * it should have both of them.
198 	 */
199 	if (mod && !check_data_sec && !tracepoint_data_sec)
200 		return 0;
201 
202 	if (!check_data_sec) {
203 		if (mod) {
204 			fprintf(stderr, "warning: Module %s has only unused tracepoints\n", fname);
205 			/* Do not fail build */
206 			return 0;
207 		}
208 		fprintf(stderr,	"no __tracepoint_check in file: %s\n", fname);
209 		return -1;
210 	}
211 
212 	if (!tracepoint_data_sec) {
213 		fprintf(stderr,	"no __tracepoint_strings in file: %s\n", fname);
214 		return -1;
215 	}
216 
217 	if (!mod)
218 		fname = NULL;
219 
220 	etrace.ehdr = ehdr;
221 	tracepoint_check(&etrace, fname);
222 	return 0;
223 }
224 
main(int argc,char * argv[])225 int main(int argc, char *argv[])
226 {
227 	int n_error = 0;
228 	size_t size = 0;
229 	void *addr = NULL;
230 	bool mod = false;
231 
232 	if (argc > 1 && strcmp(argv[1], "--module") == 0) {
233 		mod = true;
234 		argc--;
235 		argv++;
236 	}
237 
238 	if (argc < 2) {
239 		if (mod)
240 			fprintf(stderr, "usage: tracepoint-update --module module...\n");
241 		else
242 			fprintf(stderr, "usage: tracepoint-update vmlinux...\n");
243 		return 0;
244 	}
245 
246 	/* Process each file in turn, allowing deep failure. */
247 	for (int i = 1; i < argc; i++) {
248 		addr = elf_map(argv[i], &size, 1 << ET_REL);
249 		if (!addr) {
250 			++n_error;
251 			continue;
252 		}
253 
254 		if (process_tracepoints(mod, addr, argv[i]))
255 			++n_error;
256 
257 		elf_unmap(addr, size);
258 	}
259 
260 	return !!n_error;
261 }
262