1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * in kernel monitor support: allows rv to control in-kernel monitors. 4 * 5 * Copyright (C) 2022 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> 6 */ 7 #include <getopt.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <errno.h> 11 #include <unistd.h> 12 13 #include <trace.h> 14 #include <utils.h> 15 #include <rv.h> 16 17 static int config_has_id; 18 static int config_my_pid; 19 static int config_trace; 20 21 static char *config_initial_reactor; 22 static char *config_reactor; 23 24 /* 25 * __ikm_read_enable - reads monitor's enable status 26 * 27 * __does not log errors. 28 * 29 * Returns the current status, or -1 if the monitor does not exist, 30 * __hence not logging errors. 31 */ 32 static int __ikm_read_enable(char *monitor_name) 33 { 34 char path[MAX_PATH]; 35 long long enabled; 36 int retval; 37 38 snprintf(path, MAX_PATH, "rv/monitors/%s/enable", monitor_name); 39 40 retval = tracefs_instance_file_read_number(NULL, path, &enabled); 41 if (retval < 0) 42 return -1; 43 44 return enabled; 45 } 46 47 /* 48 * ikm_read_enable - reads monitor's enable status 49 * 50 * Returns the current status, or -1 on error. 51 */ 52 static int ikm_read_enable(char *monitor_name) 53 { 54 int enabled; 55 56 enabled = __ikm_read_enable(monitor_name); 57 if (enabled < 0) { 58 err_msg("ikm: fail read enabled: %d\n", enabled); 59 return -1; 60 } 61 62 debug_msg("ikm: read enabled: %d\n", enabled); 63 64 return enabled; 65 } 66 67 /* 68 * ikm_write_enable - write to the monitor's enable file 69 * 70 * Return the number of bytes written, -1 on error. 71 */ 72 static int ikm_write_enable(char *monitor_name, char *enable_disable) 73 { 74 char path[MAX_PATH]; 75 int retval; 76 77 debug_msg("ikm: writing enabled: %s\n", enable_disable); 78 79 snprintf(path, MAX_PATH, "rv/monitors/%s/enable", monitor_name); 80 retval = tracefs_instance_file_write(NULL, path, enable_disable); 81 if (retval < strlen(enable_disable)) { 82 err_msg("ikm: writing enabled: %s\n", enable_disable); 83 return -1; 84 } 85 86 return retval; 87 } 88 89 /* 90 * ikm_enable - enable a monitor 91 * 92 * Returns -1 on failure. Success otherwise. 93 */ 94 static int ikm_enable(char *monitor_name) 95 { 96 return ikm_write_enable(monitor_name, "1"); 97 } 98 99 /* 100 * ikm_disable - disable a monitor 101 * 102 * Returns -1 on failure. Success otherwise. 103 */ 104 static int ikm_disable(char *monitor_name) 105 { 106 return ikm_write_enable(monitor_name, ""); 107 } 108 109 /* 110 * ikm_read_desc - read monitors' description 111 * 112 * Return a dynamically allocated string with the monitor's 113 * description, NULL otherwise. 114 */ 115 static char *ikm_read_desc(char *monitor_name) 116 { 117 char path[MAX_PATH]; 118 char *desc; 119 120 snprintf(path, MAX_PATH, "rv/monitors/%s/desc", monitor_name); 121 desc = tracefs_instance_file_read(NULL, path, NULL); 122 if (!desc) { 123 err_msg("ikm: error reading monitor %s desc\n", monitor_name); 124 return NULL; 125 } 126 127 *strstr(desc, "\n") = '\0'; 128 129 return desc; 130 } 131 132 /* 133 * ikm_fill_monitor_definition - fill monitor's definition 134 * 135 * Returns -1 on error, 0 otherwise. 136 */ 137 static int ikm_fill_monitor_definition(char *name, struct monitor *ikm) 138 { 139 int enabled; 140 char *desc; 141 142 enabled = ikm_read_enable(name); 143 if (enabled < 0) { 144 err_msg("ikm: monitor %s fail to read enable file, bug?\n", name); 145 return -1; 146 } 147 148 desc = ikm_read_desc(name); 149 if (!desc) { 150 err_msg("ikm: monitor %s does not have desc file, bug?\n", name); 151 return -1; 152 } 153 154 strncpy(ikm->name, name, MAX_DA_NAME_LEN); 155 ikm->enabled = enabled; 156 strncpy(ikm->desc, desc, MAX_DESCRIPTION); 157 158 free(desc); 159 160 return 0; 161 } 162 163 /* 164 * ikm_write_reactor - switch the reactor to *reactor 165 * 166 * Return the number or characters written, -1 on error. 167 */ 168 static int ikm_write_reactor(char *monitor_name, char *reactor) 169 { 170 char path[MAX_PATH]; 171 int retval; 172 173 snprintf(path, MAX_PATH, "rv/monitors/%s/reactors", monitor_name); 174 retval = tracefs_instance_file_write(NULL, path, reactor); 175 debug_msg("ikm: write \"%s\" reactors: %d\n", reactor, retval); 176 177 return retval; 178 } 179 180 /* 181 * ikm_read_reactor - read the reactors file 182 * 183 * Returns a dynamically allocated string with monitor's 184 * available reactors, or NULL on error. 185 */ 186 static char *ikm_read_reactor(char *monitor_name) 187 { 188 char path[MAX_PATH]; 189 char *reactors; 190 191 snprintf(path, MAX_PATH, "rv/monitors/%s/reactors", monitor_name); 192 reactors = tracefs_instance_file_read(NULL, path, NULL); 193 if (!reactors) { 194 err_msg("ikm: fail reading monitor's %s reactors file\n", monitor_name); 195 return NULL; 196 } 197 198 return reactors; 199 } 200 201 /* 202 * ikm_get_current_reactor - get the current enabled reactor 203 * 204 * Reads the reactors file and find the currently enabled 205 * [reactor]. 206 * 207 * Returns a dynamically allocated memory with the current 208 * reactor. NULL otherwise. 209 */ 210 static char *ikm_get_current_reactor(char *monitor_name) 211 { 212 char *reactors = ikm_read_reactor(monitor_name); 213 char *curr_reactor = NULL; 214 char *start; 215 char *end; 216 217 if (!reactors) 218 return NULL; 219 220 start = strstr(reactors, "["); 221 if (!start) 222 goto out_free; 223 224 start++; 225 226 end = strstr(start, "]"); 227 if (!end) 228 goto out_free; 229 230 *end = '\0'; 231 232 curr_reactor = calloc(strlen(start) + 1, sizeof(char)); 233 if (!curr_reactor) 234 goto out_free; 235 236 strncpy(curr_reactor, start, strlen(start)); 237 debug_msg("ikm: read current reactor %s\n", curr_reactor); 238 239 out_free: 240 free(reactors); 241 242 return curr_reactor; 243 } 244 245 static int ikm_has_id(char *monitor_name) 246 { 247 char path[MAX_PATH]; 248 char *format; 249 int has_id; 250 251 snprintf(path, MAX_PATH, "events/rv/event_%s/format", monitor_name); 252 format = tracefs_instance_file_read(NULL, path, NULL); 253 if (!format) { 254 err_msg("ikm: fail reading monitor's %s format event file\n", monitor_name); 255 return -1; 256 } 257 258 /* print fmt: "%d: %s x %s -> %s %s", REC->id, ... */ 259 has_id = !!strstr(format, "REC->id"); 260 261 debug_msg("ikm: monitor %s has id: %s\n", monitor_name, has_id ? "yes" : "no"); 262 263 free(format); 264 265 return has_id; 266 } 267 268 /** 269 * ikm_list_monitors - list all available monitors 270 * 271 * Returns 0 on success, -1 otherwise. 272 */ 273 int ikm_list_monitors(void) 274 { 275 char *available_monitors; 276 struct monitor ikm; 277 char *curr, *next; 278 int retval; 279 280 available_monitors = tracefs_instance_file_read(NULL, "rv/available_monitors", NULL); 281 282 if (!available_monitors) { 283 err_msg("ikm: available monitors is not available, is CONFIG_RV enabled?\n"); 284 return -1; 285 } 286 287 curr = available_monitors; 288 do { 289 next = strstr(curr, "\n"); 290 *next = '\0'; 291 292 retval = ikm_fill_monitor_definition(curr, &ikm); 293 if (retval) 294 err_msg("ikm: error reading %d in kernel monitor, skipping\n", curr); 295 296 printf("%-24s %s %s\n", ikm.name, ikm.desc, ikm.enabled ? "[ON]" : "[OFF]"); 297 curr = ++next; 298 299 } while (strlen(curr)); 300 301 free(available_monitors); 302 303 return 0; 304 } 305 306 static void ikm_print_header(struct trace_seq *s) 307 { 308 trace_seq_printf(s, "%16s-%-8s %5s %5s ", "<TASK>", "PID", "[CPU]", "TYPE"); 309 if (config_has_id) 310 trace_seq_printf(s, "%8s ", "ID"); 311 312 trace_seq_printf(s, "%24s x %-24s -> %-24s %s\n", 313 "STATE", 314 "EVENT", 315 "NEXT_STATE", 316 "FINAL"); 317 318 trace_seq_printf(s, "%16s %-8s %5s %5s ", " | ", " | ", " | ", " | "); 319 320 if (config_has_id) 321 trace_seq_printf(s, "%8s ", " | "); 322 323 trace_seq_printf(s, "%24s %-24s %-24s %s\n", 324 " | ", 325 " | ", 326 " | ", 327 "|"); 328 329 } 330 331 /* 332 * ikm_event_handler - callback to handle event events 333 * 334 * Called any time a rv:"monitor"_event events is generated. 335 * It parses and print event. 336 */ 337 static int 338 ikm_event_handler(struct trace_seq *s, struct tep_record *record, 339 struct tep_event *trace_event, void *context) 340 { 341 /* if needed: struct trace_instance *inst = context; */ 342 char *state, *event, *next_state; 343 unsigned long long final_state; 344 unsigned long long pid; 345 unsigned long long id; 346 int cpu = record->cpu; 347 int val; 348 349 if (config_has_id) 350 tep_get_field_val(s, trace_event, "id", record, &id, 1); 351 352 tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1); 353 354 if (config_has_id && (config_my_pid == id)) 355 return 0; 356 else if (config_my_pid && (config_my_pid == pid)) 357 return 0; 358 359 tep_print_event(trace_event->tep, s, record, "%16s-%-8d ", TEP_PRINT_COMM, TEP_PRINT_PID); 360 361 trace_seq_printf(s, "[%.3d] event ", cpu); 362 363 if (config_has_id) 364 trace_seq_printf(s, "%8llu ", id); 365 366 state = tep_get_field_raw(s, trace_event, "state", record, &val, 0); 367 event = tep_get_field_raw(s, trace_event, "event", record, &val, 0); 368 next_state = tep_get_field_raw(s, trace_event, "next_state", record, &val, 0); 369 tep_get_field_val(s, trace_event, "final_state", record, &final_state, 1); 370 371 trace_seq_printf(s, "%24s x %-24s -> %-24s %s\n", 372 state, 373 event, 374 next_state, 375 final_state ? "Y" : "N"); 376 377 trace_seq_do_printf(s); 378 trace_seq_reset(s); 379 380 return 0; 381 } 382 383 /* 384 * ikm_error_handler - callback to handle error events 385 * 386 * Called any time a rv:"monitor"_errors events is generated. 387 * It parses and print event. 388 */ 389 static int 390 ikm_error_handler(struct trace_seq *s, struct tep_record *record, 391 struct tep_event *trace_event, void *context) 392 { 393 unsigned long long pid, id; 394 int cpu = record->cpu; 395 char *state, *event; 396 int val; 397 398 if (config_has_id) 399 tep_get_field_val(s, trace_event, "id", record, &id, 1); 400 401 tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1); 402 403 if (config_has_id && config_my_pid == id) 404 return 0; 405 else if (config_my_pid == pid) 406 return 0; 407 408 trace_seq_printf(s, "%8lld [%03d] error ", pid, cpu); 409 410 if (config_has_id) 411 trace_seq_printf(s, "%8llu ", id); 412 413 state = tep_get_field_raw(s, trace_event, "state", record, &val, 0); 414 event = tep_get_field_raw(s, trace_event, "event", record, &val, 0); 415 416 trace_seq_printf(s, "%24s x %s\n", state, event); 417 418 trace_seq_do_printf(s); 419 trace_seq_reset(s); 420 421 return 0; 422 } 423 424 /* 425 * ikm_setup_trace_instance - set up a tracing instance to collect data 426 * 427 * Create a trace instance, enable rv: events and enable the trace. 428 * 429 * Returns the trace_instance * with all set, NULL otherwise. 430 */ 431 static struct trace_instance *ikm_setup_trace_instance(char *monitor_name) 432 { 433 char event[MAX_DA_NAME_LEN + 7]; /* max(error_,event_) + '' = 7 */ 434 struct trace_instance *inst; 435 int retval; 436 437 if (!config_trace) 438 return NULL; 439 440 config_has_id = ikm_has_id(monitor_name); 441 if (config_has_id < 0) { 442 err_msg("ikm: failed to read monitor %s event format\n", monitor_name); 443 goto out_err; 444 } 445 446 /* alloc data */ 447 inst = calloc(1, sizeof(*inst)); 448 if (!inst) { 449 err_msg("ikm: failed to allocate trace instance"); 450 goto out_err; 451 } 452 453 retval = trace_instance_init(inst, monitor_name); 454 if (retval) 455 goto out_free; 456 457 /* enable events */ 458 snprintf(event, sizeof(event), "event_%s", monitor_name); 459 retval = tracefs_event_enable(inst->inst, "rv", event); 460 if (retval) 461 goto out_inst; 462 463 tep_register_event_handler(inst->tep, -1, "rv", event, 464 ikm_event_handler, NULL); 465 466 snprintf(event, sizeof(event), "error_%s", monitor_name); 467 retval = tracefs_event_enable(inst->inst, "rv", event); 468 if (retval) 469 goto out_inst; 470 471 tep_register_event_handler(inst->tep, -1, "rv", event, 472 ikm_error_handler, NULL); 473 474 /* ready to enable */ 475 tracefs_trace_on(inst->inst); 476 477 return inst; 478 479 out_inst: 480 trace_instance_destroy(inst); 481 out_free: 482 free(inst); 483 out_err: 484 return NULL; 485 } 486 487 /** 488 * ikm_destroy_trace_instance - destroy a previously created instance 489 */ 490 static void ikm_destroy_trace_instance(struct trace_instance *inst) 491 { 492 if (!inst) 493 return; 494 495 trace_instance_destroy(inst); 496 free(inst); 497 } 498 499 /* 500 * ikm_usage_print_reactors - print all available reactors, one per line. 501 */ 502 static void ikm_usage_print_reactors(void) 503 { 504 char *reactors = tracefs_instance_file_read(NULL, "rv/available_reactors", NULL); 505 char *start, *end; 506 507 if (!reactors) 508 return; 509 510 fprintf(stderr, " available reactors:"); 511 512 start = reactors; 513 end = strstr(start, "\n"); 514 515 while (end) { 516 *end = '\0'; 517 518 fprintf(stderr, " %s", start); 519 520 start = ++end; 521 end = strstr(start, "\n"); 522 } 523 524 fprintf(stderr, "\n"); 525 } 526 /* 527 * ikm_usage - print usage 528 */ 529 static void ikm_usage(int exit_val, char *monitor_name, const char *fmt, ...) 530 { 531 532 char message[1024]; 533 va_list ap; 534 int i; 535 536 static const char *const usage[] = { 537 "", 538 " -h/--help: print this menu and the reactor list", 539 " -r/--reactor 'reactor': enables the 'reactor'", 540 " -s/--self: when tracing (-t), also trace rv command", 541 " -t/--trace: trace monitor's event", 542 " -v/--verbose: print debug messages", 543 "", 544 NULL, 545 }; 546 547 va_start(ap, fmt); 548 vsnprintf(message, sizeof(message), fmt, ap); 549 va_end(ap); 550 551 fprintf(stderr, " %s\n", message); 552 553 fprintf(stderr, "\n usage: rv mon %s [-h] [-q] [-r reactor] [-s] [-v]", monitor_name); 554 555 for (i = 0; usage[i]; i++) 556 fprintf(stderr, "%s\n", usage[i]); 557 558 ikm_usage_print_reactors(); 559 exit(exit_val); 560 } 561 562 /* 563 * parse_arguments - parse arguments and set config 564 */ 565 static int parse_arguments(char *monitor_name, int argc, char **argv) 566 { 567 int c, retval; 568 569 config_my_pid = getpid(); 570 571 while (1) { 572 static struct option long_options[] = { 573 {"help", no_argument, 0, 'h'}, 574 {"reactor", required_argument, 0, 'r'}, 575 {"self", no_argument, 0, 's'}, 576 {"trace", no_argument, 0, 't'}, 577 {"verbose", no_argument, 0, 'v'}, 578 {0, 0, 0, 0} 579 }; 580 581 /* getopt_long stores the option index here. */ 582 int option_index = 0; 583 584 c = getopt_long(argc, argv, "hr:stv", long_options, &option_index); 585 586 /* detect the end of the options. */ 587 if (c == -1) 588 break; 589 590 switch (c) { 591 case 'h': 592 ikm_usage(0, monitor_name, "help:"); 593 break; 594 case 'r': 595 config_reactor = optarg; 596 break; 597 case 's': 598 config_my_pid = 0; 599 break; 600 case 't': 601 config_trace = 1; 602 break; 603 case 'v': 604 config_debug = 1; 605 break; 606 } 607 } 608 609 if (config_reactor) { 610 config_initial_reactor = ikm_get_current_reactor(monitor_name); 611 if (!config_initial_reactor) 612 ikm_usage(1, monitor_name, 613 "ikm: failed to read current reactor, are reactors enabled?"); 614 615 retval = ikm_write_reactor(monitor_name, config_reactor); 616 if (retval <= 0) 617 ikm_usage(1, monitor_name, 618 "ikm: failed to set %s reactor, is it available?", 619 config_reactor); 620 } 621 622 debug_msg("ikm: my pid is %d\n", config_my_pid); 623 624 return 0; 625 } 626 627 /** 628 * ikm_run_monitor - apply configs and run the monitor 629 * 630 * Returns 1 if a monitor was found an executed, 0 if no 631 * monitors were found, or -1 on error. 632 */ 633 int ikm_run_monitor(char *monitor_name, int argc, char **argv) 634 { 635 struct trace_instance *inst = NULL; 636 int retval; 637 638 /* 639 * Check if monitor exists by seeing it is enabled. 640 */ 641 retval = __ikm_read_enable(monitor_name); 642 if (retval < 0) 643 return 0; 644 645 if (retval) { 646 err_msg("ikm: monitor %s (in-kernel) is already enabled\n", monitor_name); 647 return -1; 648 } 649 650 /* we should be good to go */ 651 retval = parse_arguments(monitor_name, argc, argv); 652 if (retval) 653 ikm_usage(1, monitor_name, "ikm: failed parsing arguments"); 654 655 if (config_trace) { 656 inst = ikm_setup_trace_instance(monitor_name); 657 if (!inst) 658 return -1; 659 } 660 661 retval = ikm_enable(monitor_name); 662 if (retval < 0) 663 goto out_free_instance; 664 665 if (config_trace) 666 ikm_print_header(inst->seq); 667 668 while (!should_stop()) { 669 if (config_trace) { 670 retval = tracefs_iterate_raw_events(inst->tep, 671 inst->inst, 672 NULL, 673 0, 674 collect_registered_events, 675 inst); 676 if (retval) { 677 err_msg("ikm: error reading trace buffer\n"); 678 break; 679 } 680 } 681 682 sleep(1); 683 } 684 685 ikm_disable(monitor_name); 686 ikm_destroy_trace_instance(inst); 687 688 if (config_reactor && config_initial_reactor) 689 ikm_write_reactor(monitor_name, config_initial_reactor); 690 691 return 1; 692 693 out_free_instance: 694 ikm_destroy_trace_instance(inst); 695 if (config_reactor && config_initial_reactor) 696 ikm_write_reactor(monitor_name, config_initial_reactor); 697 return -1; 698 } 699
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.