699 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 17static int config_has_id;18static int config_my_pid;19static int config_trace;20 21static char *config_initial_reactor;22static char *config_reactor;23 24/*25 * __ikm_read_enable - reads monitor's enable status26 *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 */32static 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 status49 *50 * Returns the current status, or -1 on error.51 */52static 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 file69 *70 * Return the number of bytes written, -1 on error.71 */72static 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 monitor91 *92 * Returns -1 on failure. Success otherwise.93 */94static int ikm_enable(char *monitor_name)95{96 return ikm_write_enable(monitor_name, "1");97}98 99/*100 * ikm_disable - disable a monitor101 *102 * Returns -1 on failure. Success otherwise.103 */104static int ikm_disable(char *monitor_name)105{106 return ikm_write_enable(monitor_name, "0");107}108 109/*110 * ikm_read_desc - read monitors' description111 *112 * Return a dynamically allocated string with the monitor's113 * description, NULL otherwise.114 */115static 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 definition134 *135 * Returns -1 on error, 0 otherwise.136 */137static 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 *reactor165 *166 * Return the number or characters written, -1 on error.167 */168static 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 file182 *183 * Returns a dynamically allocated string with monitor's184 * available reactors, or NULL on error.185 */186static 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 reactor203 *204 * Reads the reactors file and find the currently enabled205 * [reactor].206 *207 * Returns a dynamically allocated memory with the current208 * reactor. NULL otherwise.209 */210static 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 239out_free:240 free(reactors);241 242 return curr_reactor;243}244 245static 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 monitors270 *271 * Returns 0 on success, -1 otherwise.272 */273int 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 306static 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 events333 *334 * Called any time a rv:"monitor"_event events is generated.335 * It parses and print event.336 */337static int338ikm_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 events385 *386 * Called any time a rv:"monitor"_errors events is generated.387 * It parses and print event.388 */389static int390ikm_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 data426 *427 * Create a trace instance, enable rv: events and enable the trace.428 *429 * Returns the trace_instance * with all set, NULL otherwise.430 */431static struct trace_instance *ikm_setup_trace_instance(char *monitor_name)432{433 char event[MAX_DA_NAME_LEN + 7]; /* max(error_,event_) + '0' = 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 479out_inst:480 trace_instance_destroy(inst);481out_free:482 free(inst);483out_err:484 return NULL;485}486 487/**488 * ikm_destroy_trace_instance - destroy a previously created instance489 */490static 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 */502static 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 usage528 */529static 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 config564 */565static 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 monitor629 *630 * Returns 1 if a monitor was found an executed, 0 if no631 * monitors were found, or -1 on error.632 */633int 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 693out_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