xref: /linux/scripts/tracepoint-update.c (revision e30f8e61e2518a837837daa26cda3c8cc30f3226)
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 
20 static inline void *get_index(void *start, int entsize, int index)
21 {
22 	return start + (entsize * index);
23 }
24 
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 
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 
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 
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 
115 static void check_tracepoints(struct elf_tracepoint *etrace)
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.\n", str);
133 		}
134 	}
135 
136 	free(etrace->array);
137 }
138 
139 static void *tracepoint_check(struct elf_tracepoint *etrace)
140 {
141 	make_trace_array(etrace);
142 	check_tracepoints(etrace);
143 
144 	return NULL;
145 }
146 
147 static int process_tracepoints(void *addr, char const *const fname)
148 {
149 	struct elf_tracepoint etrace = {0};
150 	Elf_Ehdr *ehdr = addr;
151 	Elf_Shdr *shdr_start;
152 	Elf_Shdr *string_sec;
153 	const char *secstrings;
154 	unsigned int shnum;
155 	unsigned int shstrndx;
156 	int shentsize;
157 	int idx;
158 	int done = 2;
159 
160 	shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
161 	shentsize = ehdr_shentsize(ehdr);
162 
163 	shstrndx = ehdr_shstrndx(ehdr);
164 	if (shstrndx == SHN_XINDEX)
165 		shstrndx = shdr_link(shdr_start);
166 	string_sec = get_index(shdr_start, shentsize, shstrndx);
167 	secstrings = (const char *)ehdr + shdr_offset(string_sec);
168 
169 	shnum = ehdr_shnum(ehdr);
170 	if (shnum == SHN_UNDEF)
171 		shnum = shdr_size(shdr_start);
172 
173 	for (int i = 0; done && i < shnum; i++) {
174 		Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
175 
176 		idx = shdr_name(shdr);
177 
178 		/* locate the __tracepoint_check in vmlinux */
179 		if (!strcmp(secstrings + idx, "__tracepoint_check")) {
180 			check_data_sec = shdr;
181 			done--;
182 		}
183 
184 		/* locate the __tracepoints_ptrs section in vmlinux */
185 		if (!strcmp(secstrings + idx, "__tracepoints_strings")) {
186 			tracepoint_data_sec = shdr;
187 			done--;
188 		}
189 	}
190 
191 	if (!check_data_sec) {
192 		fprintf(stderr,	"no __tracepoint_check in file: %s\n", fname);
193 		return -1;
194 	}
195 
196 	if (!tracepoint_data_sec) {
197 		fprintf(stderr,	"no __tracepoint_strings in file: %s\n", fname);
198 		return -1;
199 	}
200 
201 	etrace.ehdr = ehdr;
202 	tracepoint_check(&etrace);
203 	return 0;
204 }
205 
206 int main(int argc, char *argv[])
207 {
208 	int n_error = 0;
209 	size_t size = 0;
210 	void *addr = NULL;
211 
212 	if (argc < 2) {
213 		fprintf(stderr, "usage: tracepoint-update vmlinux...\n");
214 		return 0;
215 	}
216 
217 	/* Process each file in turn, allowing deep failure. */
218 	for (int i = 1; i < argc; i++) {
219 		addr = elf_map(argv[i], &size, 1 << ET_REL);
220 		if (!addr) {
221 			++n_error;
222 			continue;
223 		}
224 
225 		if (process_tracepoints(addr, argv[i]))
226 			++n_error;
227 
228 		elf_unmap(addr, size);
229 	}
230 
231 	return !!n_error;
232 }
233