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 
13 static char ftrace_path[] = "ksft-ftrace-XXXXXX";
14 static bool ftrace_mounted;
15 uint64_t ns_cookie1, ns_cookie2;
16 
17 struct 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 
37 static struct test_ftracer *ftracers;
38 static pthread_mutex_t ftracers_lock = PTHREAD_MUTEX_INITIALIZER;
39 
mount_ftrace(void)40 static 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 
unmount_ftrace(void)53 static 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 
63 struct opts_list_t {
64 	char *opt_name;
65 	struct opts_list_t *next;
66 };
67 
disable_trace_options(const char * ftrace_path)68 static 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 
119 out_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);
129 out_free:
130 	free(fopts);
131 	return ret;
132 }
133 
setup_buffer_size(const char * ftrace_path,size_t sz)134 static 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 
setup_ftrace_instance(struct test_ftracer * tracer,const char * name)147 static 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 
remove_ftrace_instance(struct test_ftracer * tracer)164 static 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 
tracer_cleanup(void * arg)172 static void tracer_cleanup(void *arg)
173 {
174 	struct test_ftracer *tracer = arg;
175 
176 	fclose(tracer->trace_pipe);
177 }
178 
tracer_set_error(struct test_ftracer * tracer,int error)179 static void tracer_set_error(struct test_ftracer *tracer, int error)
180 {
181 	if (!tracer->error)
182 		tracer->error = error;
183 }
184 
tracer_get_savedlines_nr(struct test_ftracer * tracer)185 const size_t tracer_get_savedlines_nr(struct test_ftracer *tracer)
186 {
187 	return tracer->next_line_ind;
188 }
189 
tracer_get_savedlines(struct test_ftracer * tracer)190 const char **tracer_get_savedlines(struct test_ftracer *tracer)
191 {
192 	return (const char **)tracer->saved_lines;
193 }
194 
tracer_thread_func(void * arg)195 static 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 
setup_trace_thread(struct test_ftracer * tracer)239 static 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 
260 out_free:
261 	free(path);
262 	return ret;
263 }
264 
stop_trace_thread(struct test_ftracer * tracer)265 static 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 
final_wait_for_events(struct test_ftracer * tracer,unsigned timeout_sec)285 static 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 
setup_trace_event(struct test_ftracer * tracer,const char * event,const char * filter)306 int 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 
326 out_free:
327 	free(filter_path);
328 	free(enable_path);
329 	return ret;
330 }
331 
create_ftracer(const char * name,enum ftracer_op (* process_line)(const char * line),void (* destructor)(struct test_ftracer * tracer),bool (* expecting_more)(void),size_t lines_buf_sz,size_t buffer_size_kb)332 struct 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 
408 err_mutex_destroy:
409 	pthread_mutex_destroy(&tracer->met_all_expected_lock);
410 err_cond_destroy:
411 	pthread_cond_destroy(&tracer->met_all_expected);
412 err_free_lines:
413 	free(tracer->saved_lines);
414 err_remove:
415 	remove_ftrace_instance(tracer);
416 err_free:
417 	free(tracer);
418 	return NULL;
419 }
420 
__destroy_ftracer(struct test_ftracer * tracer)421 static 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 
destroy_ftracer(struct test_ftracer * tracer)437 void 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 
destroy_all_ftracers(void)457 static 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 
test_unset_tracing(void)475 static void test_unset_tracing(void)
476 {
477 	destroy_all_ftracers();
478 	unmount_ftrace();
479 }
480 
test_setup_tracing(void)481 int 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 
get_ns_cookie(int nsfd,uint64_t * out)514 static 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 
test_init_ftrace(int nsfd1,int nsfd2)537 void 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