Path: blob/master/tools/verification/rv/src/in_kernel.c
26285 views
// SPDX-License-Identifier: GPL-2.01/*2* in kernel monitor support: allows rv to control in-kernel monitors.3*4* Copyright (C) 2022 Red Hat Inc, Daniel Bristot de Oliveira <[email protected]>5*/6#include <getopt.h>7#include <stdlib.h>8#include <stdio.h>9#include <string.h>10#include <errno.h>11#include <unistd.h>12#include <dirent.h>1314#include <trace.h>15#include <utils.h>16#include <rv.h>1718static int config_has_id;19static int config_is_container;20static int config_my_pid;21static int config_trace;2223static char *config_initial_reactor;24static char *config_reactor;2526/*27* __ikm_read_enable - reads monitor's enable status28*29* __does not log errors.30*31* Returns the current status, or -1 if the monitor does not exist,32* __hence not logging errors.33*/34static int __ikm_read_enable(char *monitor_name)35{36char path[MAX_PATH];37long long enabled;38int retval;3940snprintf(path, MAX_PATH, "rv/monitors/%s/enable", monitor_name);4142retval = tracefs_instance_file_read_number(NULL, path, &enabled);43if (retval < 0)44return -1;4546return enabled;47}4849/*50* __ikm_find_monitor - find the full name of a possibly nested module51*52* __does not log errors.53*54* Returns 1 if we found the monitor, -1 on error and 0 if it does not exist.55* The string out_name is populated with the full name, which can be56* equal to monitor_name or container/monitor_name if nested57*/58static int __ikm_find_monitor_name(char *monitor_name, char *out_name)59{60char *available_monitors, container[MAX_DA_NAME_LEN+1], *cursor, *end;61int retval = 1;6263available_monitors = tracefs_instance_file_read(NULL, "rv/available_monitors", NULL);64if (!available_monitors)65return -1;6667cursor = strstr(available_monitors, monitor_name);68if (!cursor) {69retval = 0;70goto out_free;71}7273for (; cursor > available_monitors; cursor--)74if (*(cursor-1) == '\n')75break;76end = strstr(cursor, "\n");77memcpy(out_name, cursor, end-cursor);78out_name[end-cursor] = '\0';7980cursor = strstr(out_name, ":");81if (cursor)82*cursor = '/';83else {84sprintf(container, "%s:", monitor_name);85if (strstr(available_monitors, container))86config_is_container = 1;87}8889out_free:90free(available_monitors);91return retval;92}9394/*95* ikm_read_enable - reads monitor's enable status96*97* Returns the current status, or -1 on error.98*/99static int ikm_read_enable(char *monitor_name)100{101int enabled;102103enabled = __ikm_read_enable(monitor_name);104if (enabled < 0) {105err_msg("ikm: fail read enabled: %d\n", enabled);106return -1;107}108109debug_msg("ikm: read enabled: %d\n", enabled);110111return enabled;112}113114/*115* ikm_write_enable - write to the monitor's enable file116*117* Return the number of bytes written, -1 on error.118*/119static int ikm_write_enable(char *monitor_name, char *enable_disable)120{121char path[MAX_PATH];122int retval;123124debug_msg("ikm: writing enabled: %s\n", enable_disable);125126snprintf(path, MAX_PATH, "rv/monitors/%s/enable", monitor_name);127retval = tracefs_instance_file_write(NULL, path, enable_disable);128if (retval < strlen(enable_disable)) {129err_msg("ikm: writing enabled: %s\n", enable_disable);130return -1;131}132133return retval;134}135136/*137* ikm_enable - enable a monitor138*139* Returns -1 on failure. Success otherwise.140*/141static int ikm_enable(char *monitor_name)142{143return ikm_write_enable(monitor_name, "1");144}145146/*147* ikm_disable - disable a monitor148*149* Returns -1 on failure. Success otherwise.150*/151static int ikm_disable(char *monitor_name)152{153return ikm_write_enable(monitor_name, "0");154}155156/*157* ikm_read_desc - read monitors' description158*159* Return a dynamically allocated string with the monitor's160* description, NULL otherwise.161*/162static char *ikm_read_desc(char *monitor_name)163{164char path[MAX_PATH];165char *desc;166167snprintf(path, MAX_PATH, "rv/monitors/%s/desc", monitor_name);168desc = tracefs_instance_file_read(NULL, path, NULL);169if (!desc) {170err_msg("ikm: error reading monitor %s desc\n", monitor_name);171return NULL;172}173174*strstr(desc, "\n") = '\0';175176return desc;177}178179/*180* ikm_fill_monitor_definition - fill monitor's definition181*182* Returns -1 on error, 1 if the monitor does not belong in the container, 0 otherwise.183* container can be NULL184*/185static int ikm_fill_monitor_definition(char *name, struct monitor *ikm, char *container)186{187int enabled;188char *desc, *nested_name;189190nested_name = strstr(name, ":");191if (nested_name) {192/* it belongs in container if it starts with "container:" */193if (container && strstr(name, container) != name)194return 1;195*nested_name = '/';196++nested_name;197ikm->nested = 1;198} else {199if (container)200return 1;201nested_name = name;202ikm->nested = 0;203}204205enabled = ikm_read_enable(name);206if (enabled < 0) {207err_msg("ikm: monitor %s fail to read enable file, bug?\n", name);208return -1;209}210211desc = ikm_read_desc(name);212if (!desc) {213err_msg("ikm: monitor %s does not have desc file, bug?\n", name);214return -1;215}216217strncpy(ikm->name, nested_name, MAX_DA_NAME_LEN);218ikm->enabled = enabled;219strncpy(ikm->desc, desc, MAX_DESCRIPTION);220221free(desc);222223return 0;224}225226/*227* ikm_write_reactor - switch the reactor to *reactor228*229* Return the number or characters written, -1 on error.230*/231static int ikm_write_reactor(char *monitor_name, char *reactor)232{233char path[MAX_PATH];234int retval;235236snprintf(path, MAX_PATH, "rv/monitors/%s/reactors", monitor_name);237retval = tracefs_instance_file_write(NULL, path, reactor);238debug_msg("ikm: write \"%s\" reactors: %d\n", reactor, retval);239240return retval;241}242243/*244* ikm_read_reactor - read the reactors file245*246* Returns a dynamically allocated string with monitor's247* available reactors, or NULL on error.248*/249static char *ikm_read_reactor(char *monitor_name)250{251char path[MAX_PATH];252char *reactors;253254snprintf(path, MAX_PATH, "rv/monitors/%s/reactors", monitor_name);255reactors = tracefs_instance_file_read(NULL, path, NULL);256if (!reactors) {257err_msg("ikm: fail reading monitor's %s reactors file\n", monitor_name);258return NULL;259}260261return reactors;262}263264/*265* ikm_get_current_reactor - get the current enabled reactor266*267* Reads the reactors file and find the currently enabled268* [reactor].269*270* Returns a dynamically allocated memory with the current271* reactor. NULL otherwise.272*/273static char *ikm_get_current_reactor(char *monitor_name)274{275char *reactors = ikm_read_reactor(monitor_name);276char *curr_reactor = NULL;277char *start;278char *end;279280if (!reactors)281return NULL;282283start = strstr(reactors, "[");284if (!start)285goto out_free;286287start++;288289end = strstr(start, "]");290if (!end)291goto out_free;292293*end = '\0';294295curr_reactor = calloc(strlen(start) + 1, sizeof(char));296if (!curr_reactor)297goto out_free;298299strncpy(curr_reactor, start, strlen(start));300debug_msg("ikm: read current reactor %s\n", curr_reactor);301302out_free:303free(reactors);304305return curr_reactor;306}307308static int ikm_has_id(char *monitor_name)309{310char path[MAX_PATH];311char *format;312int has_id;313314snprintf(path, MAX_PATH, "events/rv/event_%s/format", monitor_name);315format = tracefs_instance_file_read(NULL, path, NULL);316if (!format) {317err_msg("ikm: fail reading monitor's %s format event file\n", monitor_name);318return -1;319}320321/* print fmt: "%d: %s x %s -> %s %s", REC->id, ... */322has_id = !!strstr(format, "REC->id");323324debug_msg("ikm: monitor %s has id: %s\n", monitor_name, has_id ? "yes" : "no");325326free(format);327328return has_id;329}330331/**332* ikm_list_monitors - list all available monitors333*334* Returns 0 on success, -1 otherwise.335*/336int ikm_list_monitors(char *container)337{338char *available_monitors;339struct monitor ikm = {0};340char *curr, *next;341int retval, list_monitor = 0;342343available_monitors = tracefs_instance_file_read(NULL, "rv/available_monitors", NULL);344345if (!available_monitors) {346err_msg("ikm: available monitors is not available, is CONFIG_RV enabled?\n");347return -1;348}349350curr = available_monitors;351do {352next = strstr(curr, "\n");353*next = '\0';354355retval = ikm_fill_monitor_definition(curr, &ikm, container);356if (retval < 0)357err_msg("ikm: error reading %d in kernel monitor, skipping\n", curr);358359if (!retval) {360int indent = ikm.nested && !container;361362list_monitor = 1;363printf("%s%-*s %s %s\n", indent ? " - " : "",364indent ? MAX_DA_NAME_LEN - 3 : MAX_DA_NAME_LEN,365ikm.name, ikm.desc, ikm.enabled ? "[ON]" : "[OFF]");366}367curr = ++next;368369} while (strlen(curr));370371if (!list_monitor) {372if (container)373printf("-- No monitor found in container %s --\n", container);374else375printf("-- No monitor found --\n");376}377378free(available_monitors);379380return 0;381}382383static void ikm_print_header(struct trace_seq *s)384{385trace_seq_printf(s, "%16s-%-8s %5s %5s ", "<TASK>", "PID", "[CPU]", "TYPE");386if (config_has_id)387trace_seq_printf(s, "%8s ", "ID");388389trace_seq_printf(s, "%24s x %-24s -> %-24s %s\n",390"STATE",391"EVENT",392"NEXT_STATE",393"FINAL");394395trace_seq_printf(s, "%16s %-8s %5s %5s ", " | ", " | ", " | ", " | ");396397if (config_has_id)398trace_seq_printf(s, "%8s ", " | ");399400trace_seq_printf(s, "%24s %-24s %-24s %s\n",401" | ",402" | ",403" | ",404"|");405406}407408/*409* ikm_event_handler - callback to handle event events410*411* Called any time a rv:"monitor"_event events is generated.412* It parses and prints event.413*/414static int415ikm_event_handler(struct trace_seq *s, struct tep_record *record,416struct tep_event *trace_event, void *context)417{418/* if needed: struct trace_instance *inst = context; */419char *state, *event, *next_state;420unsigned long long final_state;421unsigned long long pid;422unsigned long long id;423int val;424bool missing_id;425426if (config_has_id)427missing_id = tep_get_field_val(s, trace_event, "id", record, &id, 1);428429tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1);430431if (config_has_id && (config_my_pid == id))432return 0;433else if (config_my_pid == pid)434return 0;435436tep_print_event(trace_event->tep, s, record, "%16s-%-8d [%.3d] ",437TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU);438439if (config_is_container)440tep_print_event(trace_event->tep, s, record, "%s ", TEP_PRINT_NAME);441else442trace_seq_printf(s, "event ");443444if (config_has_id) {445if (missing_id)446/* placeholder if we are dealing with a mixed-type container*/447trace_seq_printf(s, " ");448else449trace_seq_printf(s, "%8llu ", id);450}451452state = tep_get_field_raw(s, trace_event, "state", record, &val, 0);453event = tep_get_field_raw(s, trace_event, "event", record, &val, 0);454next_state = tep_get_field_raw(s, trace_event, "next_state", record, &val, 0);455tep_get_field_val(s, trace_event, "final_state", record, &final_state, 1);456457trace_seq_printf(s, "%24s x %-24s -> %-24s %s\n",458state,459event,460next_state,461final_state ? "Y" : "N");462463trace_seq_do_printf(s);464trace_seq_reset(s);465466return 0;467}468469/*470* ikm_error_handler - callback to handle error events471*472* Called any time a rv:"monitor"_errors events is generated.473* It parses and prints event.474*/475static int476ikm_error_handler(struct trace_seq *s, struct tep_record *record,477struct tep_event *trace_event, void *context)478{479unsigned long long pid, id;480int cpu = record->cpu;481char *state, *event;482int val;483bool missing_id;484485if (config_has_id)486missing_id = tep_get_field_val(s, trace_event, "id", record, &id, 1);487488tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1);489490if (config_has_id && config_my_pid == id)491return 0;492else if (config_my_pid == pid)493return 0;494495trace_seq_printf(s, "%8lld [%03d] ", pid, cpu);496497if (config_is_container)498tep_print_event(trace_event->tep, s, record, "%s ", TEP_PRINT_NAME);499else500trace_seq_printf(s, "error ");501502if (config_has_id) {503if (missing_id)504/* placeholder if we are dealing with a mixed-type container*/505trace_seq_printf(s, " ");506else507trace_seq_printf(s, "%8llu ", id);508}509510state = tep_get_field_raw(s, trace_event, "state", record, &val, 0);511event = tep_get_field_raw(s, trace_event, "event", record, &val, 0);512513trace_seq_printf(s, "%24s x %s\n", state, event);514515trace_seq_do_printf(s);516trace_seq_reset(s);517518return 0;519}520521static int ikm_enable_trace_events(char *monitor_name, struct trace_instance *inst)522{523char event[MAX_DA_NAME_LEN + 7]; /* max(error_,event_) + '0' = 7 */524int retval;525526snprintf(event, sizeof(event), "event_%s", monitor_name);527retval = tracefs_event_enable(inst->inst, "rv", event);528if (retval)529return -1;530531tep_register_event_handler(inst->tep, -1, "rv", event,532ikm_event_handler, NULL);533534snprintf(event, sizeof(event), "error_%s", monitor_name);535retval = tracefs_event_enable(inst->inst, "rv", event);536if (retval)537return -1;538539tep_register_event_handler(inst->tep, -1, "rv", event,540ikm_error_handler, NULL);541542/* set if at least 1 monitor has id in case of a container */543config_has_id = ikm_has_id(monitor_name);544if (config_has_id < 0)545return -1;546547548return 0;549}550551static int ikm_enable_trace_container(char *monitor_name,552struct trace_instance *inst)553{554DIR *dp;555char *abs_path, rv_path[MAX_PATH];556struct dirent *ep;557int retval = 0;558559snprintf(rv_path, MAX_PATH, "rv/monitors/%s", monitor_name);560abs_path = tracefs_instance_get_file(NULL, rv_path);561if (!abs_path)562return -1;563dp = opendir(abs_path);564if (!dp)565goto out_free;566567while (!retval && (ep = readdir(dp))) {568if (ep->d_type != DT_DIR || ep->d_name[0] == '.')569continue;570retval = ikm_enable_trace_events(ep->d_name, inst);571}572573closedir(dp);574out_free:575free(abs_path);576return retval;577}578579/*580* ikm_setup_trace_instance - set up a tracing instance to collect data581*582* Create a trace instance, enable rv: events and enable the trace.583*584* Returns the trace_instance * with all set, NULL otherwise.585*/586static struct trace_instance *ikm_setup_trace_instance(char *monitor_name)587{588struct trace_instance *inst;589int retval;590591if (!config_trace)592return NULL;593594/* alloc data */595inst = calloc(1, sizeof(*inst));596if (!inst) {597err_msg("ikm: failed to allocate trace instance");598goto out_err;599}600601retval = trace_instance_init(inst, monitor_name);602if (retval)603goto out_free;604605if (config_is_container)606retval = ikm_enable_trace_container(monitor_name, inst);607else608retval = ikm_enable_trace_events(monitor_name, inst);609if (retval)610goto out_inst;611612/* ready to enable */613tracefs_trace_on(inst->inst);614615return inst;616617out_inst:618trace_instance_destroy(inst);619out_free:620free(inst);621out_err:622return NULL;623}624625/**626* ikm_destroy_trace_instance - destroy a previously created instance627*/628static void ikm_destroy_trace_instance(struct trace_instance *inst)629{630if (!inst)631return;632633trace_instance_destroy(inst);634free(inst);635}636637/*638* ikm_usage_print_reactors - print all available reactors, one per line.639*/640static void ikm_usage_print_reactors(void)641{642char *reactors = tracefs_instance_file_read(NULL, "rv/available_reactors", NULL);643char *start, *end;644645if (!reactors)646return;647648fprintf(stderr, " available reactors:");649650start = reactors;651end = strstr(start, "\n");652653while (end) {654*end = '\0';655656fprintf(stderr, " %s", start);657658start = ++end;659end = strstr(start, "\n");660}661662fprintf(stderr, "\n");663}664/*665* ikm_usage - print usage666*/667static void ikm_usage(int exit_val, char *monitor_name, const char *fmt, ...)668{669670char message[1024];671va_list ap;672int i;673674static const char *const usage[] = {675"",676" -h/--help: print this menu and the reactor list",677" -r/--reactor 'reactor': enables the 'reactor'",678" -s/--self: when tracing (-t), also trace rv command",679" -t/--trace: trace monitor's event",680" -v/--verbose: print debug messages",681"",682NULL,683};684685va_start(ap, fmt);686vsnprintf(message, sizeof(message), fmt, ap);687va_end(ap);688689fprintf(stderr, " %s\n", message);690691fprintf(stderr, "\n usage: rv mon %s [-h] [-q] [-r reactor] [-s] [-v]", monitor_name);692693for (i = 0; usage[i]; i++)694fprintf(stderr, "%s\n", usage[i]);695696ikm_usage_print_reactors();697exit(exit_val);698}699700/*701* parse_arguments - parse arguments and set config702*/703static int parse_arguments(char *monitor_name, int argc, char **argv)704{705int c, retval;706707config_my_pid = getpid();708709while (1) {710static struct option long_options[] = {711{"help", no_argument, 0, 'h'},712{"reactor", required_argument, 0, 'r'},713{"self", no_argument, 0, 's'},714{"trace", no_argument, 0, 't'},715{"verbose", no_argument, 0, 'v'},716{0, 0, 0, 0}717};718719/* getopt_long stores the option index here. */720int option_index = 0;721722c = getopt_long(argc, argv, "hr:stv", long_options, &option_index);723724/* detect the end of the options. */725if (c == -1)726break;727728switch (c) {729case 'h':730ikm_usage(0, monitor_name, "help:");731break;732case 'r':733config_reactor = optarg;734break;735case 's':736config_my_pid = -1;737break;738case 't':739config_trace = 1;740break;741case 'v':742config_debug = 1;743break;744}745}746747if (config_reactor) {748config_initial_reactor = ikm_get_current_reactor(monitor_name);749if (!config_initial_reactor)750ikm_usage(1, monitor_name,751"ikm: failed to read current reactor, are reactors enabled?");752753retval = ikm_write_reactor(monitor_name, config_reactor);754if (retval <= 0)755ikm_usage(1, monitor_name,756"ikm: failed to set %s reactor, is it available?",757config_reactor);758}759760debug_msg("ikm: my pid is %d\n", config_my_pid);761762return 0;763}764765/**766* ikm_run_monitor - apply configs and run the monitor767*768* Returns 1 if a monitor was found an executed, 0 if no769* monitors were found, or -1 on error.770*/771int ikm_run_monitor(char *monitor_name, int argc, char **argv)772{773struct trace_instance *inst = NULL;774char *nested_name, full_name[2*MAX_DA_NAME_LEN];775int retval;776777nested_name = strstr(monitor_name, ":");778if (nested_name)779++nested_name;780else781nested_name = monitor_name;782783retval = __ikm_find_monitor_name(monitor_name, full_name);784if (!retval)785return 0;786if (retval < 0) {787err_msg("ikm: error finding monitor %s\n", nested_name);788return -1;789}790791retval = __ikm_read_enable(full_name);792if (retval) {793err_msg("ikm: monitor %s (in-kernel) is already enabled\n", nested_name);794return -1;795}796797/* we should be good to go */798retval = parse_arguments(full_name, argc, argv);799if (retval)800ikm_usage(1, nested_name, "ikm: failed parsing arguments");801802if (config_trace) {803inst = ikm_setup_trace_instance(nested_name);804if (!inst)805return -1;806}807808retval = ikm_enable(full_name);809if (retval < 0)810goto out_free_instance;811812if (config_trace)813ikm_print_header(inst->seq);814815while (!should_stop()) {816if (config_trace) {817retval = tracefs_iterate_raw_events(inst->tep,818inst->inst,819NULL,8200,821collect_registered_events,822inst);823if (retval) {824err_msg("ikm: error reading trace buffer\n");825break;826}827}828829sleep(1);830}831832ikm_disable(full_name);833ikm_destroy_trace_instance(inst);834835if (config_reactor && config_initial_reactor)836ikm_write_reactor(full_name, config_initial_reactor);837838return 1;839840out_free_instance:841ikm_destroy_trace_instance(inst);842if (config_reactor && config_initial_reactor)843ikm_write_reactor(full_name, config_initial_reactor);844return -1;845}846847848