File Index
Files and directories
Sign in
FILE

tracepoint-update.c

File preview with syntax highlighting.

linux-6.19.14/scripts/tracepoint-update.c C
  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
 17static Elf_Shdr *check_data_sec;
 18static Elf_Shdr *tracepoint_data_sec;
 19
 20static inline void *get_index(void *start, int entsize, int index)
 21{
 22	return start + (entsize * index);
 23}
 24
 25static 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
 33struct 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
 42static 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
 81static 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
112static 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
117static 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
145static 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
153static 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
230int 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}
~/ Read-only file preview.