[RFC][PATCH 19/21] tracing: Add 'onmatch' hist trigger action support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add an 'onmatch().trace(event)' hist trigger action which is invoked
with the set of resolved variables named in the given synthetic event.
The result is the generation of a synthetic event that consists of the
values contained in those variables at the time the invoking event was
hit.

As an example the below defines a simple synthetic event using a
variable defined on the sched_wakeup_new event, and shows the event
definition with unresolved fields, since the sched_wakeup_new event
with the testpid variable hasn't been defined yet:

    # echo 'wakeup_new_test pid=sched_wakeup_new:testpid' >> \
      /sys/kernel/debug/tracing/synthetic_events

    # cat /sys/kernel/debug/tracing/synthetic_events
      wakeup_new_test pid=sched_wakeup_new:testpid*

The following hist trigger both defines the missing testpid variable
and specifies an onmatch().trace action that generates a
wakeup_new_test synthetic event whenever a sched_wakeup_new event
occurs, which because of the 'if comm == "cyclictest"' filter only
happens when the executable is cyclictest:

    # echo 'hist:keys=testpid=pid:onmatch().trace(wakeup_new_test) \
      if comm=="cyclictest"' >> \
      /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/trigger

Creating and displaying a histogram based on those events is now just
a matter of using the fields and new synthetic event in the
tracing/events/synthetic directory, as usual:

    # echo 'hist:keys=pid:sort=pid' >> \
      /sys/kernel/debug/tracing/events/synthetic/wakeup_new_test/trigger

Signed-off-by: Tom Zanussi <tom.zanussi@xxxxxxxxxxxxxxx>
---
 kernel/trace/trace_events_hist.c | 167 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 167 insertions(+)

diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 46da09f..2f9efb8 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -279,6 +279,7 @@ static u64 hist_field_timestamp(struct hist_field *hist_field,
 }
 
 static LIST_HEAD(hist_var_list);
+static LIST_HEAD(hist_action_list);
 
 struct hist_var_data {
 	struct list_head list;
@@ -610,6 +611,16 @@ static int parse_action(char *str, struct hist_trigger_attrs *attrs)
 	if (attrs->n_actions == HIST_ACTIONS_MAX)
 		return -EINVAL;
 
+	if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0)) {
+		attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL);
+		if (!attrs->action_str[attrs->n_actions]) {
+			ret = -ENOMEM;
+			return ret;
+		}
+		attrs->n_actions++;
+		ret = 1;
+	}
+
 	return ret;
 }
 
@@ -1823,6 +1834,129 @@ static int add_synthetic_var_refs(struct hist_trigger_data *hist_data,
 	return var_ref_idx;
 }
 
+static void action_trace(struct hist_trigger_data *hist_data,
+			 struct tracing_map_elt *elt, void *rec,
+			 struct ring_buffer_event *rbe,
+			 struct action_data *data, u64 *var_ref_vals)
+{
+	struct synthetic_event *event = data->synthetic_event;
+
+	trace_synthetic(event, var_ref_vals, data->var_ref_idx);
+}
+
+static bool check_hist_action_refs(struct hist_trigger_data *hist_data,
+				   struct synthetic_event *event)
+{
+	unsigned int i;
+
+	for (i = 0; i < hist_data->n_actions; i++) {
+		struct action_data *data = hist_data->actions[i];
+
+		if (data->fn == action_trace && data->synthetic_event == event)
+			return true;
+	}
+
+	return false;
+}
+
+static bool check_synthetic_action_refs(struct synthetic_event *event)
+{
+	struct hist_var_data *var_data;
+
+	list_for_each_entry(var_data, &hist_action_list, list)
+		if (check_hist_action_refs(var_data->hist_data, event))
+			return true;
+
+	return false;
+}
+
+static struct hist_var_data *find_hist_actions(struct hist_trigger_data *hist_data)
+{
+	struct hist_var_data *var_data, *found = NULL;
+
+	list_for_each_entry(var_data, &hist_action_list, list) {
+		if (var_data->hist_data == hist_data) {
+			found = var_data;
+			break;
+		}
+	}
+
+	return found;
+}
+
+static int save_hist_actions(struct hist_trigger_data *hist_data)
+{
+	struct hist_var_data *var_data;
+
+	var_data = find_hist_actions(hist_data);
+	if (var_data)
+		return 0;
+
+	var_data = kzalloc(sizeof(*var_data), GFP_KERNEL);
+	if (!var_data)
+		return -ENOMEM;
+
+	var_data->hist_data = hist_data;
+	list_add(&var_data->list, &hist_action_list);
+
+	return 0;
+}
+
+static int remove_hist_actions(struct hist_trigger_data *hist_data)
+{
+	struct hist_var_data *var_data;
+
+	var_data = find_hist_actions(hist_data);
+	if (!var_data)
+		return -EINVAL;
+
+	list_del(&var_data->list);
+
+	return 0;
+}
+
+static int create_onmatch_data(char *str, struct hist_trigger_data *hist_data)
+{
+	char *fn_name, *param;
+	struct action_data *data;
+	int ret = 0;
+
+	strsep(&str, ".");
+	if (!str)
+		return -EINVAL;
+
+	fn_name = strsep(&str, "(");
+	if (!fn_name || !str)
+		return -EINVAL;
+
+	if (strncmp(fn_name, "trace", strlen("trace")) == 0) {
+		struct synthetic_event *event;
+
+		param = strsep(&str, ")");
+		if (!param)
+			return -EINVAL;
+
+		event = find_synthetic_event(param);
+		if (!event)
+			return -EINVAL;
+
+		if (!resolve_pending_var_refs(event))
+			return -EINVAL;
+
+		data = kzalloc(sizeof(*data), GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		data->fn = action_trace;
+		data->synthetic_event = event;
+		data->var_ref_idx = add_synthetic_var_refs(hist_data, event);
+		hist_data->actions[hist_data->n_actions++] = data;
+		save_hist_actions(hist_data);
+	}
+
+	return ret;
+}
+
 static void destroy_actions(struct hist_trigger_data *hist_data)
 {
 	unsigned int i;
@@ -1842,6 +1976,14 @@ static int create_actions(struct hist_trigger_data *hist_data)
 
 	for (i = 0; i < hist_data->attrs->n_actions; i++) {
 		str = hist_data->attrs->action_str[i];
+
+		if (strncmp(str, "onmatch(", strlen("onmatch(")) == 0) {
+			char *action_str = str + strlen("onmatch(");
+
+			ret = create_onmatch_data(action_str, hist_data);
+			if (ret)
+				return ret;
+		}
 	}
 
 	return ret;
@@ -1858,6 +2000,16 @@ static void print_actions(struct seq_file *m,
 	}
 }
 
+static void print_onmatch_spec(struct seq_file *m,
+			       struct hist_trigger_data *hist_data,
+			       struct action_data *data)
+{
+	seq_puts(m, ":onmatch().");
+
+	if (data->synthetic_event)
+		seq_printf(m, "trace(%s)", data->synthetic_event->name);
+}
+
 static void print_actions_spec(struct seq_file *m,
 			       struct hist_trigger_data *hist_data)
 {
@@ -1865,6 +2017,9 @@ static void print_actions_spec(struct seq_file *m,
 
 	for (i = 0; i < hist_data->n_actions; i++) {
 		struct action_data *data = hist_data->actions[i];
+
+		if (data->fn == action_trace)
+			print_onmatch_spec(m, hist_data, data);
 	}
 }
 
@@ -2428,6 +2583,9 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops,
 		if (remove_hist_vars(hist_data))
 			return;
 
+		if (remove_hist_actions(hist_data))
+			return;
+
 		destroy_hist_data(hist_data);
 	}
 }
@@ -3417,6 +3575,10 @@ static int create_synthetic_event(int argc, char **argv)
 	event = find_synthetic_event(token);
 	if (event) {
 		if (delete_event) {
+			if (check_synthetic_action_refs(event)) {
+				ret = -EINVAL;
+				goto out;
+			}
 			remove_synthetic_event(event);
 			goto err;
 		} else
@@ -3462,6 +3624,11 @@ static int release_all_synthetic_events(void)
 
 	mutex_lock(&synthetic_event_mutex);
 
+	list_for_each_entry(event, &synthetic_events_list, list) {
+		if (check_synthetic_action_refs(event))
+			return -EINVAL;
+	}
+
 	list_for_each_entry_safe(event, e, &synthetic_events_list, list) {
 		remove_synthetic_event(event);
 		free_synthetic_event(event);
-- 
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-rt-users" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [RT Stable]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]

  Powered by Linux