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