@@ -7,6 +7,7 @@ struct monitor {
char name[MAX_DA_NAME_LEN];
char desc[MAX_DESCRIPTION];
int enabled;
+ int nested;
};
int should_stop(void);
@@ -6,15 +6,18 @@
*/
#include <getopt.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
+#include <dirent.h>
#include <trace.h>
#include <utils.h>
#include <rv.h>
static int config_has_id;
+static int config_is_container;
static int config_my_pid;
static int config_trace;
@@ -44,6 +47,51 @@ static int __ikm_read_enable(char *monitor_name)
return enabled;
}
+/*
+ * __ikm_find_monitor - find the full name of a possibly nested module
+ *
+ * __does not log errors.
+ *
+ * Returns 1 if we found the monitor, -1 on error and 0 if it does not exist.
+ * The string out_name is populated with the full name, which can be
+ * equal to monitor_name or container/monitor_name if nested
+ */
+static int __ikm_find_monitor_name(char *monitor_name, char *out_name)
+{
+ char *available_monitors, container[MAX_DA_NAME_LEN+1], *cursor, *end;
+ int retval = 1;
+
+ available_monitors = tracefs_instance_file_read(NULL, "rv/available_monitors", NULL);
+ if (!available_monitors)
+ return -1;
+
+ cursor = strstr(available_monitors, monitor_name);
+ if (!cursor) {
+ retval = 0;
+ goto out_free;
+ }
+
+ for (; cursor > available_monitors; cursor--)
+ if (*(cursor-1) == '\n')
+ break;
+ end = strstr(cursor, "\n");
+ memcpy(out_name, cursor, end-cursor);
+ out_name[end-cursor] = '\0';
+
+ cursor = strstr(out_name, ":");
+ if (cursor)
+ *cursor = '/';
+ else {
+ sprintf(container, "%s:", monitor_name);
+ if (strstr(available_monitors, container))
+ config_is_container = 1;
+ }
+
+out_free:
+ free(available_monitors);
+ return retval;
+}
+
/*
* ikm_read_enable - reads monitor's enable status
*
@@ -137,7 +185,17 @@ static char *ikm_read_desc(char *monitor_name)
static int ikm_fill_monitor_definition(char *name, struct monitor *ikm)
{
int enabled;
- char *desc;
+ char *desc, *nested_name;
+
+ nested_name = strstr(name, ":");
+ if (nested_name) {
+ *nested_name = '/';
+ ++nested_name;
+ ikm->nested = 1;
+ } else {
+ nested_name = name;
+ ikm->nested = 0;
+ }
enabled = ikm_read_enable(name);
if (enabled < 0) {
@@ -151,7 +209,7 @@ static int ikm_fill_monitor_definition(char *name, struct monitor *ikm)
return -1;
}
- strncpy(ikm->name, name, MAX_DA_NAME_LEN);
+ strncpy(ikm->name, nested_name, MAX_DA_NAME_LEN);
ikm->enabled = enabled;
strncpy(ikm->desc, desc, MAX_DESCRIPTION);
@@ -273,7 +331,7 @@ static int ikm_has_id(char *monitor_name)
int ikm_list_monitors(void)
{
char *available_monitors;
- struct monitor ikm;
+ struct monitor ikm = {0};
char *curr, *next;
int retval;
@@ -293,7 +351,9 @@ int ikm_list_monitors(void)
if (retval)
err_msg("ikm: error reading %d in kernel monitor, skipping\n", curr);
- printf("%-24s %s %s\n", ikm.name, ikm.desc, ikm.enabled ? "[ON]" : "[OFF]");
+ printf("%s%-*s %s %s\n", ikm.nested ? " - " : "",
+ ikm.nested ? MAX_DA_NAME_LEN - 3 : MAX_DA_NAME_LEN,
+ ikm.name, ikm.desc, ikm.enabled ? "[ON]" : "[OFF]");
curr = ++next;
} while (strlen(curr));
@@ -343,11 +403,11 @@ ikm_event_handler(struct trace_seq *s, struct tep_record *record,
unsigned long long final_state;
unsigned long long pid;
unsigned long long id;
- int cpu = record->cpu;
int val;
+ bool missing_id;
if (config_has_id)
- tep_get_field_val(s, trace_event, "id", record, &id, 1);
+ missing_id = tep_get_field_val(s, trace_event, "id", record, &id, 1);
tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1);
@@ -356,12 +416,21 @@ ikm_event_handler(struct trace_seq *s, struct tep_record *record,
else if (config_my_pid && (config_my_pid == pid))
return 0;
- tep_print_event(trace_event->tep, s, record, "%16s-%-8d ", TEP_PRINT_COMM, TEP_PRINT_PID);
+ tep_print_event(trace_event->tep, s, record, "%16s-%-8d [%.3d] ",
+ TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU);
- trace_seq_printf(s, "[%.3d] event ", cpu);
+ if (config_is_container)
+ tep_print_event(trace_event->tep, s, record, "%s ", TEP_PRINT_NAME);
+ else
+ trace_seq_printf(s, "event ");
- if (config_has_id)
- trace_seq_printf(s, "%8llu ", id);
+ if (config_has_id) {
+ if (missing_id)
+ /* placeholder if we are dealing with a mixed-type container*/
+ trace_seq_printf(s, " ");
+ else
+ trace_seq_printf(s, "%8llu ", id);
+ }
state = tep_get_field_raw(s, trace_event, "state", record, &val, 0);
event = tep_get_field_raw(s, trace_event, "event", record, &val, 0);
@@ -394,9 +463,10 @@ ikm_error_handler(struct trace_seq *s, struct tep_record *record,
int cpu = record->cpu;
char *state, *event;
int val;
+ bool missing_id;
if (config_has_id)
- tep_get_field_val(s, trace_event, "id", record, &id, 1);
+ missing_id = tep_get_field_val(s, trace_event, "id", record, &id, 1);
tep_get_common_field_val(s, trace_event, "common_pid", record, &pid, 1);
@@ -405,10 +475,20 @@ ikm_error_handler(struct trace_seq *s, struct tep_record *record,
else if (config_my_pid == pid)
return 0;
- trace_seq_printf(s, "%8lld [%03d] error ", pid, cpu);
+ trace_seq_printf(s, "%8lld [%03d] ", pid, cpu);
- if (config_has_id)
- trace_seq_printf(s, "%8llu ", id);
+ if (config_is_container)
+ tep_print_event(trace_event->tep, s, record, "%s ", TEP_PRINT_NAME);
+ else
+ trace_seq_printf(s, "error ");
+
+ if (config_has_id) {
+ if (missing_id)
+ /* placeholder if we are dealing with a mixed-type container*/
+ trace_seq_printf(s, " ");
+ else
+ trace_seq_printf(s, "%8llu ", id);
+ }
state = tep_get_field_raw(s, trace_event, "state", record, &val, 0);
event = tep_get_field_raw(s, trace_event, "event", record, &val, 0);
@@ -421,6 +501,64 @@ ikm_error_handler(struct trace_seq *s, struct tep_record *record,
return 0;
}
+static int ikm_enable_trace_events(char *monitor_name, struct trace_instance *inst)
+{
+ char event[MAX_DA_NAME_LEN + 7]; /* max(error_,event_) + '0' = 7 */
+ int retval;
+
+ snprintf(event, sizeof(event), "event_%s", monitor_name);
+ retval = tracefs_event_enable(inst->inst, "rv", event);
+ if (retval)
+ return -1;
+
+ tep_register_event_handler(inst->tep, -1, "rv", event,
+ ikm_event_handler, NULL);
+
+ snprintf(event, sizeof(event), "error_%s", monitor_name);
+ retval = tracefs_event_enable(inst->inst, "rv", event);
+ if (retval)
+ return -1;
+
+ tep_register_event_handler(inst->tep, -1, "rv", event,
+ ikm_error_handler, NULL);
+
+ /* set if at least 1 monitor has id in case of a container */
+ config_has_id = ikm_has_id(monitor_name);
+ if (config_has_id < 0)
+ return -1;
+
+
+ return 0;
+}
+
+static int ikm_enable_trace_container(char *monitor_name,
+ struct trace_instance *inst)
+{
+ DIR *dp;
+ char *abs_path, rv_path[MAX_PATH];
+ struct dirent *ep;
+ int retval = 0;
+
+ snprintf(rv_path, MAX_PATH, "rv/monitors/%s", monitor_name);
+ abs_path = tracefs_instance_get_file(NULL, rv_path);
+ if (!abs_path)
+ return -1;
+ dp = opendir(abs_path);
+ if (!dp)
+ goto out_free;
+
+ while (!retval && (ep = readdir(dp))) {
+ if (ep->d_type != DT_DIR || ep->d_name[0] == '.')
+ continue;
+ retval = ikm_enable_trace_events(ep->d_name, inst);
+ }
+
+ closedir(dp);
+out_free:
+ free(abs_path);
+ return retval;
+}
+
/*
* ikm_setup_trace_instance - set up a tracing instance to collect data
*
@@ -430,19 +568,12 @@ ikm_error_handler(struct trace_seq *s, struct tep_record *record,
*/
static struct trace_instance *ikm_setup_trace_instance(char *monitor_name)
{
- char event[MAX_DA_NAME_LEN + 7]; /* max(error_,event_) + '0' = 7 */
struct trace_instance *inst;
int retval;
if (!config_trace)
return NULL;
- config_has_id = ikm_has_id(monitor_name);
- if (config_has_id < 0) {
- err_msg("ikm: failed to read monitor %s event format\n", monitor_name);
- goto out_err;
- }
-
/* alloc data */
inst = calloc(1, sizeof(*inst));
if (!inst) {
@@ -454,23 +585,13 @@ static struct trace_instance *ikm_setup_trace_instance(char *monitor_name)
if (retval)
goto out_free;
- /* enable events */
- snprintf(event, sizeof(event), "event_%s", monitor_name);
- retval = tracefs_event_enable(inst->inst, "rv", event);
- if (retval)
- goto out_inst;
-
- tep_register_event_handler(inst->tep, -1, "rv", event,
- ikm_event_handler, NULL);
-
- snprintf(event, sizeof(event), "error_%s", monitor_name);
- retval = tracefs_event_enable(inst->inst, "rv", event);
+ if (config_is_container)
+ retval = ikm_enable_trace_container(monitor_name, inst);
+ else
+ retval = ikm_enable_trace_events(monitor_name, inst);
if (retval)
goto out_inst;
- tep_register_event_handler(inst->tep, -1, "rv", event,
- ikm_error_handler, NULL);
-
/* ready to enable */
tracefs_trace_on(inst->inst);
@@ -633,32 +754,41 @@ static int parse_arguments(char *monitor_name, int argc, char **argv)
int ikm_run_monitor(char *monitor_name, int argc, char **argv)
{
struct trace_instance *inst = NULL;
+ char *nested_name, full_name[2*MAX_DA_NAME_LEN];
int retval;
- /*
- * Check if monitor exists by seeing it is enabled.
- */
- retval = __ikm_read_enable(monitor_name);
- if (retval < 0)
+ nested_name = strstr(monitor_name, ":");
+ if (nested_name)
+ ++nested_name;
+ else
+ nested_name = monitor_name;
+
+ retval = __ikm_find_monitor_name(monitor_name, full_name);
+ if (!retval)
return 0;
+ if (retval < 0) {
+ err_msg("ikm: error finding monitor %s\n", nested_name);
+ return -1;
+ }
+ retval = __ikm_read_enable(full_name);
if (retval) {
- err_msg("ikm: monitor %s (in-kernel) is already enabled\n", monitor_name);
+ err_msg("ikm: monitor %s (in-kernel) is already enabled\n", nested_name);
return -1;
}
/* we should be good to go */
- retval = parse_arguments(monitor_name, argc, argv);
+ retval = parse_arguments(full_name, argc, argv);
if (retval)
- ikm_usage(1, monitor_name, "ikm: failed parsing arguments");
+ ikm_usage(1, nested_name, "ikm: failed parsing arguments");
if (config_trace) {
- inst = ikm_setup_trace_instance(monitor_name);
+ inst = ikm_setup_trace_instance(nested_name);
if (!inst)
return -1;
}
- retval = ikm_enable(monitor_name);
+ retval = ikm_enable(full_name);
if (retval < 0)
goto out_free_instance;
@@ -682,17 +812,17 @@ int ikm_run_monitor(char *monitor_name, int argc, char **argv)
sleep(1);
}
- ikm_disable(monitor_name);
+ ikm_disable(full_name);
ikm_destroy_trace_instance(inst);
if (config_reactor && config_initial_reactor)
- ikm_write_reactor(monitor_name, config_initial_reactor);
+ ikm_write_reactor(full_name, config_initial_reactor);
return 1;
out_free_instance:
ikm_destroy_trace_instance(inst);
if (config_reactor && config_initial_reactor)
- ikm_write_reactor(monitor_name, config_initial_reactor);
+ ikm_write_reactor(full_name, config_initial_reactor);
return -1;
}
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 Signed-off-by: Gabriele Monaco <gmonaco@redhat.com> --- tools/verification/rv/include/rv.h | 1 + tools/verification/rv/src/in_kernel.c | 226 ++++++++++++++++++++------ 2 files changed, 179 insertions(+), 48 deletions(-)