File Index
Files and directories
Sign in
FILE

ftrace.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/tcp_ao/lib/ftrace.c C
  1// SPDX-License-Identifier: GPL-2.0
  2#include <inttypes.h>
  3#include <pthread.h>
  4#include <stdbool.h>
  5#include <stdio.h>
  6#include <stdlib.h>
  7#include <sys/mount.h>
  8#include <sys/time.h>
  9#include <unistd.h>
 10#include "../../../../../include/linux/kernel.h"
 11#include "aolib.h"
 12
 13static char ftrace_path[] = "ksft-ftrace-XXXXXX";
 14static bool ftrace_mounted;
 15uint64_t ns_cookie1, ns_cookie2;
 16
 17struct test_ftracer {
 18	pthread_t tracer_thread;
 19	int	error;
 20	char	*instance_path;
 21	FILE	*trace_pipe;
 22
 23	enum ftracer_op (*process_line)(const char *line);
 24	void (*destructor)(struct test_ftracer *tracer);
 25	bool (*expecting_more)(void);
 26
 27	char	**saved_lines;
 28	size_t	saved_lines_size;
 29	size_t	next_line_ind;
 30
 31	pthread_cond_t met_all_expected;
 32	pthread_mutex_t met_all_expected_lock;
 33
 34	struct test_ftracer *next;
 35};
 36
 37static struct test_ftracer *ftracers;
 38static pthread_mutex_t ftracers_lock = PTHREAD_MUTEX_INITIALIZER;
 39
 40static int mount_ftrace(void)
 41{
 42	if (!mkdtemp(ftrace_path))
 43		test_error("Can't create temp dir");
 44
 45	if (mount("tracefs", ftrace_path, "tracefs", 0, "rw"))
 46		return -errno;
 47
 48	ftrace_mounted = true;
 49
 50	return 0;
 51}
 52
 53static void unmount_ftrace(void)
 54{
 55	if (ftrace_mounted && umount(ftrace_path))
 56		test_print("Failed on cleanup: can't unmount tracefs: %m");
 57
 58	if (rmdir(ftrace_path))
 59		test_error("Failed on cleanup: can't remove ftrace dir %s",
 60			   ftrace_path);
 61}
 62
 63struct opts_list_t {
 64	char *opt_name;
 65	struct opts_list_t *next;
 66};
 67
 68static int disable_trace_options(const char *ftrace_path)
 69{
 70	struct opts_list_t *opts_list = NULL;
 71	char *fopts, *line = NULL;
 72	size_t buf_len = 0;
 73	ssize_t line_len;
 74	int ret = 0;
 75	FILE *opts;
 76
 77	fopts = test_sprintf("%s/%s", ftrace_path, "trace_options");
 78	if (!fopts)
 79		return -ENOMEM;
 80
 81	opts = fopen(fopts, "r+");
 82	if (!opts) {
 83		ret = -errno;
 84		goto out_free;
 85	}
 86
 87	while ((line_len = getline(&line, &buf_len, opts)) != -1) {
 88		struct opts_list_t *tmp;
 89
 90		if (!strncmp(line, "no", 2))
 91			continue;
 92
 93		tmp = malloc(sizeof(*tmp));
 94		if (!tmp) {
 95			ret = -ENOMEM;
 96			goto out_free_opts_list;
 97		}
 98		tmp->next = opts_list;
 99		tmp->opt_name = test_sprintf("no%s", line);
100		if (!tmp->opt_name) {
101			ret = -ENOMEM;
102			free(tmp);
103			goto out_free_opts_list;
104		}
105		opts_list = tmp;
106	}
107
108	while (opts_list) {
109		struct opts_list_t *tmp = opts_list;
110
111		fseek(opts, 0, SEEK_SET);
112		fwrite(tmp->opt_name, 1, strlen(tmp->opt_name), opts);
113
114		opts_list = opts_list->next;
115		free(tmp->opt_name);
116		free(tmp);
117	}
118
119out_free_opts_list:
120	while (opts_list) {
121		struct opts_list_t *tmp = opts_list;
122
123		opts_list = opts_list->next;
124		free(tmp->opt_name);
125		free(tmp);
126	}
127	free(line);
128	fclose(opts);
129out_free:
130	free(fopts);
131	return ret;
132}
133
134static int setup_buffer_size(const char *ftrace_path, size_t sz)
135{
136	char *fbuf_size = test_sprintf("%s/buffer_size_kb", ftrace_path);
137	int ret;
138
139	if (!fbuf_size)
140		return -1;
141
142	ret = test_echo(fbuf_size, 0, "%zu", sz);
143	free(fbuf_size);
144	return ret;
145}
146
147static int setup_ftrace_instance(struct test_ftracer *tracer, const char *name)
148{
149	char *tmp;
150
151	tmp = test_sprintf("%s/instances/ksft-%s-XXXXXX", ftrace_path, name);
152	if (!tmp)
153		return -ENOMEM;
154
155	tracer->instance_path = mkdtemp(tmp);
156	if (!tracer->instance_path) {
157		free(tmp);
158		return -errno;
159	}
160
161	return 0;
162}
163
164static void remove_ftrace_instance(struct test_ftracer *tracer)
165{
166	if (rmdir(tracer->instance_path))
167		test_print("Failed on cleanup: can't remove ftrace instance %s",
168			   tracer->instance_path);
169	free(tracer->instance_path);
170}
171
172static void tracer_cleanup(void *arg)
173{
174	struct test_ftracer *tracer = arg;
175
176	fclose(tracer->trace_pipe);
177}
178
179static void tracer_set_error(struct test_ftracer *tracer, int error)
180{
181	if (!tracer->error)
182		tracer->error = error;
183}
184
185const size_t tracer_get_savedlines_nr(struct test_ftracer *tracer)
186{
187	return tracer->next_line_ind;
188}
189
190const char **tracer_get_savedlines(struct test_ftracer *tracer)
191{
192	return (const char **)tracer->saved_lines;
193}
194
195static void *tracer_thread_func(void *arg)
196{
197	struct test_ftracer *tracer = arg;
198
199	pthread_cleanup_push(tracer_cleanup, arg);
200
201	while (tracer->next_line_ind < tracer->saved_lines_size) {
202		char **lp = &tracer->saved_lines[tracer->next_line_ind];
203		enum ftracer_op op;
204		size_t buf_len = 0;
205		ssize_t line_len;
206
207		line_len = getline(lp, &buf_len, tracer->trace_pipe);
208		if (line_len == -1)
209			break;
210
211		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
212		op = tracer->process_line(*lp);
213		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
214
215		if (tracer->expecting_more) {
216			pthread_mutex_lock(&tracer->met_all_expected_lock);
217			if (!tracer->expecting_more())
218				pthread_cond_signal(&tracer->met_all_expected);
219			pthread_mutex_unlock(&tracer->met_all_expected_lock);
220		}
221
222		if (op == FTRACER_LINE_DISCARD)
223			continue;
224		if (op == FTRACER_EXIT)
225			break;
226		if (op != FTRACER_LINE_PRESERVE)
227			test_error("unexpected tracer command %d", op);
228
229		tracer->next_line_ind++;
230		buf_len = 0;
231	}
232	test_print("too many lines in ftracer buffer %zu, exiting tracer",
233		   tracer->next_line_ind);
234
235	pthread_cleanup_pop(1);
236	return NULL;
237}
238
239static int setup_trace_thread(struct test_ftracer *tracer)
240{
241	int ret = 0;
242	char *path;
243
244	path = test_sprintf("%s/trace_pipe", tracer->instance_path);
245	if (!path)
246		return -ENOMEM;
247
248	tracer->trace_pipe = fopen(path, "r");
249	if (!tracer->trace_pipe) {
250		ret = -errno;
251		goto out_free;
252	}
253
254	if (pthread_create(&tracer->tracer_thread, NULL,
255			   tracer_thread_func, (void *)tracer)) {
256		ret = -errno;
257		fclose(tracer->trace_pipe);
258	}
259
260out_free:
261	free(path);
262	return ret;
263}
264
265static void stop_trace_thread(struct test_ftracer *tracer)
266{
267	void *res;
268
269	if (pthread_cancel(tracer->tracer_thread)) {
270		test_print("Can't stop tracer pthread: %m");
271		tracer_set_error(tracer, -errno);
272	}
273	if (pthread_join(tracer->tracer_thread, &res)) {
274		test_print("Can't join tracer pthread: %m");
275		tracer_set_error(tracer, -errno);
276	}
277	if (res != PTHREAD_CANCELED) {
278		test_print("Tracer thread wasn't canceled");
279		tracer_set_error(tracer, -errno);
280	}
281	if (tracer->error)
282		test_fail("tracer errored by %s", strerror(tracer->error));
283}
284
285static void final_wait_for_events(struct test_ftracer *tracer,
286				  unsigned timeout_sec)
287{
288	struct timespec timeout;
289	struct timeval now;
290	int ret = 0;
291
292	if (!tracer->expecting_more)
293		return;
294
295	pthread_mutex_lock(&tracer->met_all_expected_lock);
296	gettimeofday(&now, NULL);
297	timeout.tv_sec = now.tv_sec + timeout_sec;
298	timeout.tv_nsec = now.tv_usec * 1000;
299
300	while (tracer->expecting_more() && ret != ETIMEDOUT)
301		ret = pthread_cond_timedwait(&tracer->met_all_expected,
302				&tracer->met_all_expected_lock, &timeout);
303	pthread_mutex_unlock(&tracer->met_all_expected_lock);
304}
305
306int setup_trace_event(struct test_ftracer *tracer,
307		      const char *event, const char *filter)
308{
309	char *enable_path, *filter_path, *instance = tracer->instance_path;
310	int ret;
311
312	enable_path = test_sprintf("%s/events/%s/enable", instance, event);
313	if (!enable_path)
314		return -ENOMEM;
315
316	filter_path = test_sprintf("%s/events/%s/filter", instance, event);
317	if (!filter_path) {
318		ret = -ENOMEM;
319		goto out_free;
320	}
321
322	ret = test_echo(filter_path, 0, "%s", filter);
323	if (!ret)
324		ret = test_echo(enable_path, 0, "1");
325
326out_free:
327	free(filter_path);
328	free(enable_path);
329	return ret;
330}
331
332struct test_ftracer *create_ftracer(const char *name,
333				    enum ftracer_op (*process_line)(const char *line),
334				    void (*destructor)(struct test_ftracer *tracer),
335				    bool (*expecting_more)(void),
336				    size_t lines_buf_sz, size_t buffer_size_kb)
337{
338	struct test_ftracer *tracer;
339	int err;
340
341	/* XXX: separate __create_ftracer() helper and do here
342	 * if (!kernel_config_has(KCONFIG_FTRACE))
343	 *	return NULL;
344	 */
345
346	tracer = malloc(sizeof(*tracer));
347	if (!tracer) {
348		test_print("malloc()");
349		return NULL;
350	}
351
352	memset(tracer, 0, sizeof(*tracer));
353
354	err = setup_ftrace_instance(tracer, name);
355	if (err) {
356		test_print("setup_ftrace_instance(): %d", err);
357		goto err_free;
358	}
359
360	err = disable_trace_options(tracer->instance_path);
361	if (err) {
362		test_print("disable_trace_options(): %d", err);
363		goto err_remove;
364	}
365
366	err = setup_buffer_size(tracer->instance_path, buffer_size_kb);
367	if (err) {
368		test_print("disable_trace_options(): %d", err);
369		goto err_remove;
370	}
371
372	tracer->saved_lines = calloc(lines_buf_sz, sizeof(tracer->saved_lines[0]));
373	if (!tracer->saved_lines) {
374		test_print("calloc()");
375		goto err_remove;
376	}
377	tracer->saved_lines_size = lines_buf_sz;
378
379	tracer->process_line	= process_line;
380	tracer->destructor	= destructor;
381	tracer->expecting_more	= expecting_more;
382
383	err = pthread_cond_init(&tracer->met_all_expected, NULL);
384	if (err) {
385		test_print("pthread_cond_init(): %d", err);
386		goto err_free_lines;
387	}
388
389	err = pthread_mutex_init(&tracer->met_all_expected_lock, NULL);
390	if (err) {
391		test_print("pthread_mutex_init(): %d", err);
392		goto err_cond_destroy;
393	}
394
395	err = setup_trace_thread(tracer);
396	if (err) {
397		test_print("setup_trace_thread(): %d", err);
398		goto err_mutex_destroy;
399	}
400
401	pthread_mutex_lock(&ftracers_lock);
402	tracer->next = ftracers;
403	ftracers = tracer;
404	pthread_mutex_unlock(&ftracers_lock);
405
406	return tracer;
407
408err_mutex_destroy:
409	pthread_mutex_destroy(&tracer->met_all_expected_lock);
410err_cond_destroy:
411	pthread_cond_destroy(&tracer->met_all_expected);
412err_free_lines:
413	free(tracer->saved_lines);
414err_remove:
415	remove_ftrace_instance(tracer);
416err_free:
417	free(tracer);
418	return NULL;
419}
420
421static void __destroy_ftracer(struct test_ftracer *tracer)
422{
423	size_t i;
424
425	final_wait_for_events(tracer, TEST_TIMEOUT_SEC);
426	stop_trace_thread(tracer);
427	remove_ftrace_instance(tracer);
428	if (tracer->destructor)
429		tracer->destructor(tracer);
430	for (i = 0; i < tracer->saved_lines_size; i++)
431		free(tracer->saved_lines[i]);
432	pthread_cond_destroy(&tracer->met_all_expected);
433	pthread_mutex_destroy(&tracer->met_all_expected_lock);
434	free(tracer);
435}
436
437void destroy_ftracer(struct test_ftracer *tracer)
438{
439	pthread_mutex_lock(&ftracers_lock);
440	if (tracer == ftracers) {
441		ftracers = tracer->next;
442	} else {
443		struct test_ftracer *f = ftracers;
444
445		while (f->next != tracer) {
446			if (!f->next)
447				test_error("tracers list corruption or double free %p", tracer);
448			f = f->next;
449		}
450		f->next = tracer->next;
451	}
452	tracer->next = NULL;
453	pthread_mutex_unlock(&ftracers_lock);
454	__destroy_ftracer(tracer);
455}
456
457static void destroy_all_ftracers(void)
458{
459	struct test_ftracer *f;
460
461	pthread_mutex_lock(&ftracers_lock);
462	f = ftracers;
463	ftracers = NULL;
464	pthread_mutex_unlock(&ftracers_lock);
465
466	while (f) {
467		struct test_ftracer *n = f->next;
468
469		f->next = NULL;
470		__destroy_ftracer(f);
471		f = n;
472	}
473}
474
475static void test_unset_tracing(void)
476{
477	destroy_all_ftracers();
478	unmount_ftrace();
479}
480
481int test_setup_tracing(void)
482{
483	/*
484	 * Just a basic protection - this should be called only once from
485	 * lib/kconfig. Not thread safe, which is fine as it's early, before
486	 * threads are created.
487	 */
488	static int already_set;
489	int err;
490
491	if (already_set)
492		return -1;
493
494	/* Needs net-namespace cookies for filters */
495	if (ns_cookie1 == ns_cookie2) {
496		test_print("net-namespace cookies: %" PRIu64 " == %" PRIu64 ", can't set up tracing",
497			   ns_cookie1, ns_cookie2);
498		return -1;
499	}
500
501	already_set = 1;
502
503	test_add_destructor(test_unset_tracing);
504
505	err = mount_ftrace();
506	if (err) {
507		test_print("failed to mount_ftrace(): %d", err);
508		return err;
509	}
510
511	return setup_aolib_ftracer();
512}
513
514static int get_ns_cookie(int nsfd, uint64_t *out)
515{
516	int old_ns = switch_save_ns(nsfd);
517	socklen_t size = sizeof(*out);
518	int sk;
519
520	sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
521	if (sk < 0) {
522		test_print("socket(): %m");
523		return -errno;
524	}
525
526	if (getsockopt(sk, SOL_SOCKET, SO_NETNS_COOKIE, out, &size)) {
527		test_print("getsockopt(SO_NETNS_COOKIE): %m");
528		close(sk);
529		return -errno;
530	}
531
532	close(sk);
533	switch_close_ns(old_ns);
534	return 0;
535}
536
537void test_init_ftrace(int nsfd1, int nsfd2)
538{
539	get_ns_cookie(nsfd1, &ns_cookie1);
540	get_ns_cookie(nsfd2, &ns_cookie2);
541	/* Populate kernel config state */
542	kernel_config_has(KCONFIG_FTRACE);
543}
~/ Read-only file preview.