1 // SPDX-License-Identifier: GPL-2.0
10 #include "../../../../../include/linux/kernel.h"
13 static char ftrace_path
[] = "ksft-ftrace-XXXXXX";
14 static bool ftrace_mounted
;
15 uint64_t ns_cookie1
, ns_cookie2
;
18 pthread_t tracer_thread
;
23 enum ftracer_op (*process_line
)(const char *line
);
24 void (*destructor
)(struct test_ftracer
*tracer
);
25 bool (*expecting_more
)(void);
28 size_t saved_lines_size
;
31 pthread_cond_t met_all_expected
;
32 pthread_mutex_t met_all_expected_lock
;
34 struct test_ftracer
*next
;
37 static struct test_ftracer
*ftracers
;
38 static pthread_mutex_t ftracers_lock
= PTHREAD_MUTEX_INITIALIZER
;
40 static int mount_ftrace(void)
42 if (!mkdtemp(ftrace_path
))
43 test_error("Can't create temp dir");
45 if (mount("tracefs", ftrace_path
, "tracefs", 0, "rw"))
48 ftrace_mounted
= true;
53 static void unmount_ftrace(void)
55 if (ftrace_mounted
&& umount(ftrace_path
))
56 test_print("Failed on cleanup: can't unmount tracefs: %m");
58 if (rmdir(ftrace_path
))
59 test_error("Failed on cleanup: can't remove ftrace dir %s",
65 struct opts_list_t
*next
;
68 static int disable_trace_options(const char *ftrace_path
)
70 struct opts_list_t
*opts_list
= NULL
;
71 char *fopts
, *line
= NULL
;
77 fopts
= test_sprintf("%s/%s", ftrace_path
, "trace_options");
81 opts
= fopen(fopts
, "r+");
87 while ((line_len
= getline(&line
, &buf_len
, opts
)) != -1) {
88 struct opts_list_t
*tmp
;
90 if (!strncmp(line
, "no", 2))
93 tmp
= malloc(sizeof(*tmp
));
96 goto out_free_opts_list
;
98 tmp
->next
= opts_list
;
99 tmp
->opt_name
= test_sprintf("no%s", line
);
100 if (!tmp
->opt_name
) {
103 goto out_free_opts_list
;
109 struct opts_list_t
*tmp
= opts_list
;
111 fseek(opts
, 0, SEEK_SET
);
112 fwrite(tmp
->opt_name
, 1, strlen(tmp
->opt_name
), opts
);
114 opts_list
= opts_list
->next
;
121 struct opts_list_t
*tmp
= opts_list
;
123 opts_list
= opts_list
->next
;
134 static int setup_buffer_size(const char *ftrace_path
, size_t sz
)
136 char *fbuf_size
= test_sprintf("%s/buffer_size_kb", ftrace_path
);
142 ret
= test_echo(fbuf_size
, 0, "%zu", sz
);
147 static int setup_ftrace_instance(struct test_ftracer
*tracer
, const char *name
)
151 tmp
= test_sprintf("%s/instances/ksft-%s-XXXXXX", ftrace_path
, name
);
155 tracer
->instance_path
= mkdtemp(tmp
);
156 if (!tracer
->instance_path
) {
164 static void remove_ftrace_instance(struct test_ftracer
*tracer
)
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
);
172 static void tracer_cleanup(void *arg
)
174 struct test_ftracer
*tracer
= arg
;
176 fclose(tracer
->trace_pipe
);
179 static void tracer_set_error(struct test_ftracer
*tracer
, int error
)
182 tracer
->error
= error
;
185 const size_t tracer_get_savedlines_nr(struct test_ftracer
*tracer
)
187 return tracer
->next_line_ind
;
190 const char **tracer_get_savedlines(struct test_ftracer
*tracer
)
192 return (const char **)tracer
->saved_lines
;
195 static void *tracer_thread_func(void *arg
)
197 struct test_ftracer
*tracer
= arg
;
199 pthread_cleanup_push(tracer_cleanup
, arg
);
201 while (tracer
->next_line_ind
< tracer
->saved_lines_size
) {
202 char **lp
= &tracer
->saved_lines
[tracer
->next_line_ind
];
207 line_len
= getline(lp
, &buf_len
, tracer
->trace_pipe
);
211 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
212 op
= tracer
->process_line(*lp
);
213 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, NULL
);
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
);
222 if (op
== FTRACER_LINE_DISCARD
)
224 if (op
== FTRACER_EXIT
)
226 if (op
!= FTRACER_LINE_PRESERVE
)
227 test_error("unexpected tracer command %d", op
);
229 tracer
->next_line_ind
++;
232 test_print("too many lines in ftracer buffer %zu, exiting tracer",
233 tracer
->next_line_ind
);
235 pthread_cleanup_pop(1);
239 static int setup_trace_thread(struct test_ftracer
*tracer
)
244 path
= test_sprintf("%s/trace_pipe", tracer
->instance_path
);
248 tracer
->trace_pipe
= fopen(path
, "r");
249 if (!tracer
->trace_pipe
) {
254 if (pthread_create(&tracer
->tracer_thread
, NULL
,
255 tracer_thread_func
, (void *)tracer
)) {
257 fclose(tracer
->trace_pipe
);
265 static void stop_trace_thread(struct test_ftracer
*tracer
)
269 if (pthread_cancel(tracer
->tracer_thread
)) {
270 test_print("Can't stop tracer pthread: %m");
271 tracer_set_error(tracer
, -errno
);
273 if (pthread_join(tracer
->tracer_thread
, &res
)) {
274 test_print("Can't join tracer pthread: %m");
275 tracer_set_error(tracer
, -errno
);
277 if (res
!= PTHREAD_CANCELED
) {
278 test_print("Tracer thread wasn't canceled");
279 tracer_set_error(tracer
, -errno
);
282 test_fail("tracer errored by %s", strerror(tracer
->error
));
285 static void final_wait_for_events(struct test_ftracer
*tracer
,
286 unsigned timeout_sec
)
288 struct timespec timeout
;
292 if (!tracer
->expecting_more
)
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;
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
);
306 int setup_trace_event(struct test_ftracer
*tracer
,
307 const char *event
, const char *filter
)
309 char *enable_path
, *filter_path
, *instance
= tracer
->instance_path
;
312 enable_path
= test_sprintf("%s/events/%s/enable", instance
, event
);
316 filter_path
= test_sprintf("%s/events/%s/filter", instance
, event
);
322 ret
= test_echo(filter_path
, 0, "%s", filter
);
324 ret
= test_echo(enable_path
, 0, "1");
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
)
338 struct test_ftracer
*tracer
;
341 /* XXX: separate __create_ftracer() helper and do here
342 * if (!kernel_config_has(KCONFIG_FTRACE))
346 tracer
= malloc(sizeof(*tracer
));
348 test_print("malloc()");
352 memset(tracer
, 0, sizeof(*tracer
));
354 err
= setup_ftrace_instance(tracer
, name
);
356 test_print("setup_ftrace_instance(): %d", err
);
360 err
= disable_trace_options(tracer
->instance_path
);
362 test_print("disable_trace_options(): %d", err
);
366 err
= setup_buffer_size(tracer
->instance_path
, buffer_size_kb
);
368 test_print("disable_trace_options(): %d", err
);
372 tracer
->saved_lines
= calloc(lines_buf_sz
, sizeof(tracer
->saved_lines
[0]));
373 if (!tracer
->saved_lines
) {
374 test_print("calloc()");
377 tracer
->saved_lines_size
= lines_buf_sz
;
379 tracer
->process_line
= process_line
;
380 tracer
->destructor
= destructor
;
381 tracer
->expecting_more
= expecting_more
;
383 err
= pthread_cond_init(&tracer
->met_all_expected
, NULL
);
385 test_print("pthread_cond_init(): %d", err
);
389 err
= pthread_mutex_init(&tracer
->met_all_expected_lock
, NULL
);
391 test_print("pthread_mutex_init(): %d", err
);
392 goto err_cond_destroy
;
395 err
= setup_trace_thread(tracer
);
397 test_print("setup_trace_thread(): %d", err
);
398 goto err_mutex_destroy
;
401 pthread_mutex_lock(&ftracers_lock
);
402 tracer
->next
= ftracers
;
404 pthread_mutex_unlock(&ftracers_lock
);
409 pthread_mutex_destroy(&tracer
->met_all_expected_lock
);
411 pthread_cond_destroy(&tracer
->met_all_expected
);
413 free(tracer
->saved_lines
);
415 remove_ftrace_instance(tracer
);
421 static void __destroy_ftracer(struct test_ftracer
*tracer
)
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
);
437 void destroy_ftracer(struct test_ftracer
*tracer
)
439 pthread_mutex_lock(&ftracers_lock
);
440 if (tracer
== ftracers
) {
441 ftracers
= tracer
->next
;
443 struct test_ftracer
*f
= ftracers
;
445 while (f
->next
!= tracer
) {
447 test_error("tracers list corruption or double free %p", tracer
);
450 f
->next
= tracer
->next
;
453 pthread_mutex_unlock(&ftracers_lock
);
454 __destroy_ftracer(tracer
);
457 static void destroy_all_ftracers(void)
459 struct test_ftracer
*f
;
461 pthread_mutex_lock(&ftracers_lock
);
464 pthread_mutex_unlock(&ftracers_lock
);
467 struct test_ftracer
*n
= f
->next
;
470 __destroy_ftracer(f
);
475 static void test_unset_tracing(void)
477 destroy_all_ftracers();
481 int test_setup_tracing(void)
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.
488 static int already_set
;
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
);
503 test_add_destructor(test_unset_tracing
);
505 err
= mount_ftrace();
507 test_print("failed to mount_ftrace(): %d", err
);
511 return setup_aolib_ftracer();
514 static int get_ns_cookie(int nsfd
, uint64_t *out
)
516 int old_ns
= switch_save_ns(nsfd
);
517 socklen_t size
= sizeof(*out
);
520 sk
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
522 test_print("socket(): %m");
526 if (getsockopt(sk
, SOL_SOCKET
, SO_NETNS_COOKIE
, out
, &size
)) {
527 test_print("getsockopt(SO_NETNS_COOKIE): %m");
533 switch_close_ns(old_ns
);
537 void test_init_ftrace(int nsfd1
, int nsfd2
)
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
);