Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools/rv: Add support for nested monitors

RV now supports nested monitors, this functionality requires a container
monitor, which has virtually no functionality besides holding other
monitors, and nested monitors, that have a container as parent.

Nested monitors' sysfs folders are physically nested in the container's
folder, and they are listed in the available_monitors file with the
notation container:monitor.
These changes go against the assumption that each line in the
available_monitors file correspond to a folder in the rv directory,
breaking the functionality of the rv tool.

Add support for nested containers in the rv userspace tool, indenting
nested monitors while listed and allowing both the notation with and
without container name, which are equivalent:

# rv list
mon1
mon2
container:
- nested1
- nested2

## notation with container name
# rv mon container:nested1

## notation without container name
# rv mon nested1

Either way, enabling a nested monitor is the same as enabling any other
non-nested monitor.
Selecting the container with rv mon enables all the nested monitors, if
-t is passed, the trace also includes the monitor name next to the
event:

# rv mon nested1 -t
<idle>-0 [004] event state1 x event -> state2
<idle>-0 [004] error event not expected in state2

# rv mon sched -t
<idle>-0 [004] event_nested1 state1 x event -> state2
<idle>-0 [004] error_nested1 event not expected in state2

Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Juri Lelli <juri.lelli@redhat.com>
Link: https://lore.kernel.org/20250305140406.350227-7-gmonaco@redhat.com
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

authored by

Gabriele Monaco and committed by
Steven Rostedt (Google)
eba321a1 fbe6c09b

+180 -49
+1
tools/verification/rv/include/rv.h
··· 7 7 char name[MAX_DA_NAME_LEN]; 8 8 char desc[MAX_DESCRIPTION]; 9 9 int enabled; 10 + int nested; 10 11 }; 11 12 12 13 int should_stop(void);
+179 -49
tools/verification/rv/src/in_kernel.c
··· 6 6 */ 7 7 #include <getopt.h> 8 8 #include <stdlib.h> 9 + #include <stdio.h> 9 10 #include <string.h> 10 11 #include <errno.h> 11 12 #include <unistd.h> 13 + #include <dirent.h> 12 14 13 15 #include <trace.h> 14 16 #include <utils.h> 15 17 #include <rv.h> 16 18 17 19 static int config_has_id; 20 + static int config_is_container; 18 21 static int config_my_pid; 19 22 static int config_trace; 20 23 ··· 45 42 return -1; 46 43 47 44 return enabled; 45 + } 46 + 47 + /* 48 + * __ikm_find_monitor - find the full name of a possibly nested module 49 + * 50 + * __does not log errors. 51 + * 52 + * Returns 1 if we found the monitor, -1 on error and 0 if it does not exist. 53 + * The string out_name is populated with the full name, which can be 54 + * equal to monitor_name or container/monitor_name if nested 55 + */ 56 + static int __ikm_find_monitor_name(char *monitor_name, char *out_name) 57 + { 58 + char *available_monitors, container[MAX_DA_NAME_LEN+1], *cursor, *end; 59 + int retval = 1; 60 + 61 + available_monitors = tracefs_instance_file_read(NULL, "rv/available_monitors", NULL); 62 + if (!available_monitors) 63 + return -1; 64 + 65 + cursor = strstr(available_monitors, monitor_name); 66 + if (!cursor) { 67 + retval = 0; 68 + goto out_free; 69 + } 70 + 71 + for (; cursor > available_monitors; cursor--) 72 + if (*(cursor-1) == '\n') 73 + break; 74 + end = strstr(cursor, "\n"); 75 + memcpy(out_name, cursor, end-cursor); 76 + out_name[end-cursor] = '\0'; 77 + 78 + cursor = strstr(out_name, ":"); 79 + if (cursor) 80 + *cursor = '/'; 81 + else { 82 + sprintf(container, "%s:", monitor_name); 83 + if (strstr(available_monitors, container)) 84 + config_is_container = 1; 85 + } 86 + 87 + out_free: 88 + free(available_monitors); 89 + return retval; 48 90 } 49 91 50 92 /* ··· 185 137 static int ikm_fill_monitor_definition(char *name, struct monitor *ikm) 186 138 { 187 139 int enabled; 188 - char *desc; 140 + char *desc, *nested_name; 141 + 142 + nested_name = strstr(name, ":"); 143 + if (nested_name) { 144 + *nested_name = '/'; 145 + ++nested_name; 146 + ikm->nested = 1; 147 + } else { 148 + nested_name = name; 149 + ikm->nested = 0; 150 + } 189 151 190 152 enabled = ikm_read_enable(name); 191 153 if (enabled < 0) { ··· 209 151 return -1; 210 152 } 211 153 212 - strncpy(ikm->name, name, MAX_DA_NAME_LEN); 154 + strncpy(ikm->name, nested_name, MAX_DA_NAME_LEN); 213 155 ikm->enabled = enabled; 214 156 strncpy(ikm->desc, desc, MAX_DESCRIPTION); 215 157 ··· 331 273 int ikm_list_monitors(void) 332 274 { 333 275 char *available_monitors; 334 - struct monitor ikm; 276 + struct monitor ikm = {0}; 335 277 char *curr, *next; 336 278 int retval; 337 279 ··· 351 293 if (retval) 352 294 err_msg("ikm: error reading %d in kernel monitor, skipping\n", curr); 353 295 354 - printf("%-24s %s %s\n", ikm.name, ikm.desc, ikm.enabled ? "[ON]" : "[OFF]"); 296 + printf("%s%-*s %s %s\n", ikm.nested ? " - " : "", 297 + ikm.nested ? MAX_DA_NAME_LEN - 3 : MAX_DA_NAME_LEN, 298 + ikm.name, ikm.desc, ikm.enabled ? "[ON]" : "[OFF]"); 355 299 curr = ++next; 356 300 357 301 } while (strlen(curr)); ··· 403 343 unsigned long long final_state; 404 344 unsigned long long pid; 405 345 unsigned long long id; 406 - int cpu = record->cpu; 407 346 int val; 347 + bool missing_id; 408 348 409 349 if (config_has_id) 410 - tep_get_field_val(s, trace_event, "id", record, &id, 1); 350 + missing_id = tep_get_field_val(s, trace_event, "id", record, &id, 1); 411 351 412 352 tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1); 413 353 ··· 416 356 else if (config_my_pid && (config_my_pid == pid)) 417 357 return 0; 418 358 419 - tep_print_event(trace_event->tep, s, record, "%16s-%-8d ", TEP_PRINT_COMM, TEP_PRINT_PID); 359 + tep_print_event(trace_event->tep, s, record, "%16s-%-8d [%.3d] ", 360 + TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU); 420 361 421 - trace_seq_printf(s, "[%.3d] event ", cpu); 362 + if (config_is_container) 363 + tep_print_event(trace_event->tep, s, record, "%s ", TEP_PRINT_NAME); 364 + else 365 + trace_seq_printf(s, "event "); 422 366 423 - if (config_has_id) 424 - trace_seq_printf(s, "%8llu ", id); 367 + if (config_has_id) { 368 + if (missing_id) 369 + /* placeholder if we are dealing with a mixed-type container*/ 370 + trace_seq_printf(s, " "); 371 + else 372 + trace_seq_printf(s, "%8llu ", id); 373 + } 425 374 426 375 state = tep_get_field_raw(s, trace_event, "state", record, &val, 0); 427 376 event = tep_get_field_raw(s, trace_event, "event", record, &val, 0); ··· 463 394 int cpu = record->cpu; 464 395 char *state, *event; 465 396 int val; 397 + bool missing_id; 466 398 467 399 if (config_has_id) 468 - tep_get_field_val(s, trace_event, "id", record, &id, 1); 400 + missing_id = tep_get_field_val(s, trace_event, "id", record, &id, 1); 469 401 470 402 tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1); 471 403 ··· 475 405 else if (config_my_pid == pid) 476 406 return 0; 477 407 478 - trace_seq_printf(s, "%8lld [%03d] error ", pid, cpu); 408 + trace_seq_printf(s, "%8lld [%03d] ", pid, cpu); 479 409 480 - if (config_has_id) 481 - trace_seq_printf(s, "%8llu ", id); 410 + if (config_is_container) 411 + tep_print_event(trace_event->tep, s, record, "%s ", TEP_PRINT_NAME); 412 + else 413 + trace_seq_printf(s, "error "); 414 + 415 + if (config_has_id) { 416 + if (missing_id) 417 + /* placeholder if we are dealing with a mixed-type container*/ 418 + trace_seq_printf(s, " "); 419 + else 420 + trace_seq_printf(s, "%8llu ", id); 421 + } 482 422 483 423 state = tep_get_field_raw(s, trace_event, "state", record, &val, 0); 484 424 event = tep_get_field_raw(s, trace_event, "event", record, &val, 0); ··· 501 421 return 0; 502 422 } 503 423 424 + static int ikm_enable_trace_events(char *monitor_name, struct trace_instance *inst) 425 + { 426 + char event[MAX_DA_NAME_LEN + 7]; /* max(error_,event_) + '0' = 7 */ 427 + int retval; 428 + 429 + snprintf(event, sizeof(event), "event_%s", monitor_name); 430 + retval = tracefs_event_enable(inst->inst, "rv", event); 431 + if (retval) 432 + return -1; 433 + 434 + tep_register_event_handler(inst->tep, -1, "rv", event, 435 + ikm_event_handler, NULL); 436 + 437 + snprintf(event, sizeof(event), "error_%s", monitor_name); 438 + retval = tracefs_event_enable(inst->inst, "rv", event); 439 + if (retval) 440 + return -1; 441 + 442 + tep_register_event_handler(inst->tep, -1, "rv", event, 443 + ikm_error_handler, NULL); 444 + 445 + /* set if at least 1 monitor has id in case of a container */ 446 + config_has_id = ikm_has_id(monitor_name); 447 + if (config_has_id < 0) 448 + return -1; 449 + 450 + 451 + return 0; 452 + } 453 + 454 + static int ikm_enable_trace_container(char *monitor_name, 455 + struct trace_instance *inst) 456 + { 457 + DIR *dp; 458 + char *abs_path, rv_path[MAX_PATH]; 459 + struct dirent *ep; 460 + int retval = 0; 461 + 462 + snprintf(rv_path, MAX_PATH, "rv/monitors/%s", monitor_name); 463 + abs_path = tracefs_instance_get_file(NULL, rv_path); 464 + if (!abs_path) 465 + return -1; 466 + dp = opendir(abs_path); 467 + if (!dp) 468 + goto out_free; 469 + 470 + while (!retval && (ep = readdir(dp))) { 471 + if (ep->d_type != DT_DIR || ep->d_name[0] == '.') 472 + continue; 473 + retval = ikm_enable_trace_events(ep->d_name, inst); 474 + } 475 + 476 + closedir(dp); 477 + out_free: 478 + free(abs_path); 479 + return retval; 480 + } 481 + 504 482 /* 505 483 * ikm_setup_trace_instance - set up a tracing instance to collect data 506 484 * ··· 568 430 */ 569 431 static struct trace_instance *ikm_setup_trace_instance(char *monitor_name) 570 432 { 571 - char event[MAX_DA_NAME_LEN + 7]; /* max(error_,event_) + '0' = 7 */ 572 433 struct trace_instance *inst; 573 434 int retval; 574 435 575 436 if (!config_trace) 576 437 return NULL; 577 - 578 - config_has_id = ikm_has_id(monitor_name); 579 - if (config_has_id < 0) { 580 - err_msg("ikm: failed to read monitor %s event format\n", monitor_name); 581 - goto out_err; 582 - } 583 438 584 439 /* alloc data */ 585 440 inst = calloc(1, sizeof(*inst)); ··· 585 454 if (retval) 586 455 goto out_free; 587 456 588 - /* enable events */ 589 - snprintf(event, sizeof(event), "event_%s", monitor_name); 590 - retval = tracefs_event_enable(inst->inst, "rv", event); 457 + if (config_is_container) 458 + retval = ikm_enable_trace_container(monitor_name, inst); 459 + else 460 + retval = ikm_enable_trace_events(monitor_name, inst); 591 461 if (retval) 592 462 goto out_inst; 593 - 594 - tep_register_event_handler(inst->tep, -1, "rv", event, 595 - ikm_event_handler, NULL); 596 - 597 - snprintf(event, sizeof(event), "error_%s", monitor_name); 598 - retval = tracefs_event_enable(inst->inst, "rv", event); 599 - if (retval) 600 - goto out_inst; 601 - 602 - tep_register_event_handler(inst->tep, -1, "rv", event, 603 - ikm_error_handler, NULL); 604 463 605 464 /* ready to enable */ 606 465 tracefs_trace_on(inst->inst); ··· 754 633 int ikm_run_monitor(char *monitor_name, int argc, char **argv) 755 634 { 756 635 struct trace_instance *inst = NULL; 636 + char *nested_name, full_name[2*MAX_DA_NAME_LEN]; 757 637 int retval; 758 638 759 - /* 760 - * Check if monitor exists by seeing it is enabled. 761 - */ 762 - retval = __ikm_read_enable(monitor_name); 763 - if (retval < 0) 764 - return 0; 639 + nested_name = strstr(monitor_name, ":"); 640 + if (nested_name) 641 + ++nested_name; 642 + else 643 + nested_name = monitor_name; 765 644 645 + retval = __ikm_find_monitor_name(monitor_name, full_name); 646 + if (!retval) 647 + return 0; 648 + if (retval < 0) { 649 + err_msg("ikm: error finding monitor %s\n", nested_name); 650 + return -1; 651 + } 652 + 653 + retval = __ikm_read_enable(full_name); 766 654 if (retval) { 767 - err_msg("ikm: monitor %s (in-kernel) is already enabled\n", monitor_name); 655 + err_msg("ikm: monitor %s (in-kernel) is already enabled\n", nested_name); 768 656 return -1; 769 657 } 770 658 771 659 /* we should be good to go */ 772 - retval = parse_arguments(monitor_name, argc, argv); 660 + retval = parse_arguments(full_name, argc, argv); 773 661 if (retval) 774 - ikm_usage(1, monitor_name, "ikm: failed parsing arguments"); 662 + ikm_usage(1, nested_name, "ikm: failed parsing arguments"); 775 663 776 664 if (config_trace) { 777 - inst = ikm_setup_trace_instance(monitor_name); 665 + inst = ikm_setup_trace_instance(nested_name); 778 666 if (!inst) 779 667 return -1; 780 668 } 781 669 782 - retval = ikm_enable(monitor_name); 670 + retval = ikm_enable(full_name); 783 671 if (retval < 0) 784 672 goto out_free_instance; 785 673 ··· 812 682 sleep(1); 813 683 } 814 684 815 - ikm_disable(monitor_name); 685 + ikm_disable(full_name); 816 686 ikm_destroy_trace_instance(inst); 817 687 818 688 if (config_reactor && config_initial_reactor) 819 - ikm_write_reactor(monitor_name, config_initial_reactor); 689 + ikm_write_reactor(full_name, config_initial_reactor); 820 690 821 691 return 1; 822 692 823 693 out_free_instance: 824 694 ikm_destroy_trace_instance(inst); 825 695 if (config_reactor && config_initial_reactor) 826 - ikm_write_reactor(monitor_name, config_initial_reactor); 696 + ikm_write_reactor(full_name, config_initial_reactor); 827 697 return -1; 828 698 }