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 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 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 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 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 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 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 172 static void tracer_cleanup(void *arg) 173 { 174 struct test_ftracer *tracer = arg; 175 176 fclose(tracer->trace_pipe); 177 } 178 179 static void tracer_set_error(struct test_ftracer *tracer, int error) 180 { 181 if (!tracer->error) 182 tracer->error = error; 183 } 184 185 const size_t tracer_get_savedlines_nr(struct test_ftracer *tracer) 186 { 187 return tracer->next_line_ind; 188 } 189 190 const char **tracer_get_savedlines(struct test_ftracer *tracer) 191 { 192 return (const char **)tracer->saved_lines; 193 } 194 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 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 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 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 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 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 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 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 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 475 static void test_unset_tracing(void) 476 { 477 destroy_all_ftracers(); 478 unmount_ftrace(); 479 } 480 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 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 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.