brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · e4d0b17 Raw
544 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 here342	 * 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 from485	 * lib/kconfig. Not thread safe, which is fine as it's early, before486	 * 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}544