2017-07-09 11:42:39 +00:00
|
|
|
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
|
|
|
Date: Mon, 26 Jun 2017 17:49:22 -0500
|
|
|
|
Subject: [PATCH 21/32] tracing: Add hist trigger action hook
|
2017-07-16 20:28:09 +00:00
|
|
|
Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/4.11/older/patches-4.11.9-rt7.tar.xz
|
2017-07-09 11:42:39 +00:00
|
|
|
|
|
|
|
Add a hook for executing extra actions whenever a histogram entry is
|
|
|
|
added or updated.
|
|
|
|
|
|
|
|
The default 'action' when a hist entry is added to a histogram is to
|
|
|
|
update the set of values associated with it. Some applications may
|
|
|
|
want to perform additional actions at that point, such as generate
|
|
|
|
another event, or compare and save a maximum.
|
|
|
|
|
|
|
|
Add a simple framework for doing that; specific actions will be
|
|
|
|
implemented on top of it in later patches.
|
|
|
|
|
|
|
|
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
|
|
|
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
|
|
|
---
|
|
|
|
kernel/trace/trace_events_hist.c | 114 +++++++++++++++++++++++++++++++++++++--
|
|
|
|
1 file changed, 111 insertions(+), 3 deletions(-)
|
|
|
|
|
|
|
|
--- a/kernel/trace/trace_events_hist.c
|
|
|
|
+++ b/kernel/trace/trace_events_hist.c
|
|
|
|
@@ -33,6 +33,7 @@ typedef u64 (*hist_field_fn_t) (struct h
|
|
|
|
|
|
|
|
#define HIST_FIELD_OPERANDS_MAX 2
|
|
|
|
#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
|
|
|
|
+#define HIST_ACTIONS_MAX 8
|
|
|
|
|
|
|
|
enum field_op_id {
|
|
|
|
FIELD_OP_NONE,
|
|
|
|
@@ -233,6 +234,9 @@ struct hist_trigger_attrs {
|
|
|
|
|
|
|
|
char *assignment_str[TRACING_MAP_VARS_MAX];
|
|
|
|
unsigned int n_assignments;
|
|
|
|
+
|
|
|
|
+ char *action_str[HIST_ACTIONS_MAX];
|
|
|
|
+ unsigned int n_actions;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct hist_trigger_data {
|
|
|
|
@@ -252,6 +256,21 @@ struct hist_trigger_data {
|
|
|
|
bool remove;
|
|
|
|
struct hist_field *var_refs[TRACING_MAP_VARS_MAX];
|
|
|
|
unsigned int n_var_refs;
|
|
|
|
+
|
|
|
|
+ struct action_data *actions[HIST_ACTIONS_MAX];
|
|
|
|
+ unsigned int n_actions;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct action_data;
|
|
|
|
+
|
|
|
|
+typedef void (*action_fn_t) (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 action_data {
|
|
|
|
+ action_fn_t fn;
|
|
|
|
+ unsigned int var_ref_idx;
|
|
|
|
};
|
|
|
|
|
|
|
|
static u64 hist_field_timestamp(struct hist_field *hist_field,
|
|
|
|
@@ -681,6 +700,9 @@ static void destroy_hist_trigger_attrs(s
|
|
|
|
for (i = 0; i < attrs->n_assignments; i++)
|
|
|
|
kfree(attrs->assignment_str[i]);
|
|
|
|
|
|
|
|
+ for (i = 0; i < attrs->n_actions; i++)
|
|
|
|
+ kfree(attrs->action_str[i]);
|
|
|
|
+
|
|
|
|
kfree(attrs->name);
|
|
|
|
kfree(attrs->sort_key_str);
|
|
|
|
kfree(attrs->keys_str);
|
|
|
|
@@ -688,6 +710,16 @@ static void destroy_hist_trigger_attrs(s
|
|
|
|
kfree(attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
+static int parse_action(char *str, struct hist_trigger_attrs *attrs)
|
|
|
|
+{
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ if (attrs->n_actions >= HIST_ACTIONS_MAX)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
@@ -755,8 +787,9 @@ static struct hist_trigger_attrs *parse_
|
|
|
|
else if (strcmp(str, "clear") == 0)
|
|
|
|
attrs->clear = true;
|
|
|
|
else {
|
|
|
|
- ret = -EINVAL;
|
|
|
|
- goto free;
|
|
|
|
+ ret = parse_action(str, attrs);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto free;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1722,11 +1755,63 @@ static int create_sort_keys(struct hist_
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
+static void destroy_actions(struct hist_trigger_data *hist_data)
|
|
|
|
+{
|
|
|
|
+ unsigned int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < hist_data->n_actions; i++) {
|
|
|
|
+ struct action_data *data = hist_data->actions[i];
|
|
|
|
+
|
|
|
|
+ kfree(data);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int create_actions(struct hist_trigger_data *hist_data,
|
|
|
|
+ struct trace_event_file *file)
|
|
|
|
+{
|
|
|
|
+ unsigned int i;
|
|
|
|
+ int ret = 0;
|
|
|
|
+ char *str;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < hist_data->attrs->n_actions; i++) {
|
|
|
|
+ str = hist_data->attrs->action_str[i];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void print_actions(struct seq_file *m,
|
|
|
|
+ struct hist_trigger_data *hist_data,
|
|
|
|
+ struct tracing_map_elt *elt)
|
|
|
|
+{
|
|
|
|
+ unsigned int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < hist_data->n_actions; i++) {
|
|
|
|
+ struct action_data *data = hist_data->actions[i];
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void print_actions_spec(struct seq_file *m,
|
|
|
|
+ struct hist_trigger_data *hist_data)
|
|
|
|
+{
|
|
|
|
+ unsigned int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < hist_data->n_actions; i++) {
|
|
|
|
+ struct action_data *data = hist_data->actions[i];
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void destroy_hist_data(struct hist_trigger_data *hist_data)
|
|
|
|
{
|
|
|
|
+ if (!hist_data)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
destroy_hist_trigger_attrs(hist_data->attrs);
|
|
|
|
destroy_hist_fields(hist_data);
|
|
|
|
tracing_map_destroy(hist_data->map);
|
|
|
|
+
|
|
|
|
+ destroy_actions(hist_data);
|
|
|
|
+
|
|
|
|
kfree(hist_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1886,6 +1971,20 @@ static inline void add_to_key(char *comp
|
|
|
|
memcpy(compound_key + key_field->offset, key, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
+static void
|
|
|
|
+hist_trigger_actions(struct hist_trigger_data *hist_data,
|
|
|
|
+ struct tracing_map_elt *elt, void *rec,
|
|
|
|
+ struct ring_buffer_event *rbe, u64 *var_ref_vals)
|
|
|
|
+{
|
|
|
|
+ struct action_data *data;
|
|
|
|
+ unsigned int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < hist_data->n_actions; i++) {
|
|
|
|
+ data = hist_data->actions[i];
|
|
|
|
+ data->fn(hist_data, elt, rec, rbe, data, var_ref_vals);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
|
|
|
struct ring_buffer_event *rbe)
|
|
|
|
{
|
|
|
|
@@ -1941,6 +2040,9 @@ static void event_hist_trigger(struct ev
|
|
|
|
return;
|
|
|
|
|
|
|
|
hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals);
|
|
|
|
+
|
|
|
|
+ if (resolve_var_refs(hist_data, key, var_ref_vals, true))
|
|
|
|
+ hist_trigger_actions(hist_data, elt, rec, rbe, var_ref_vals);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hist_trigger_stacktrace_print(struct seq_file *m,
|
|
|
|
@@ -2278,6 +2380,8 @@ static int event_hist_trigger_print(stru
|
|
|
|
}
|
|
|
|
seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits));
|
|
|
|
|
|
|
|
+ print_actions_spec(m, hist_data);
|
|
|
|
+
|
|
|
|
if (data->filter_str)
|
|
|
|
seq_printf(m, " if %s", data->filter_str);
|
|
|
|
|
|
|
|
@@ -2740,6 +2844,10 @@ static int event_hist_trigger_func(struc
|
|
|
|
if (has_hist_vars(hist_data))
|
|
|
|
save_hist_vars(hist_data);
|
|
|
|
|
|
|
|
+ ret = create_actions(hist_data, file);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out_unreg;
|
|
|
|
+
|
|
|
|
ret = tracing_map_init(hist_data->map);
|
|
|
|
if (ret)
|
|
|
|
goto out_unreg;
|
|
|
|
@@ -2761,8 +2869,8 @@ static int event_hist_trigger_func(struc
|
|
|
|
remove_hist_vars(hist_data);
|
|
|
|
|
|
|
|
kfree(trigger_data);
|
|
|
|
-
|
|
|
|
destroy_hist_data(hist_data);
|
|
|
|
+
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|