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