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