linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Tom Zanussi <tom.zanussi@linux.intel.com>
To: rostedt@goodmis.org
Cc: tglx@linutronix.de, mhiramat@kernel.org, namhyung@kernel.org,
	vedang.patel@intel.com, bigeasy@linutronix.de,
	joel.opensrc@gmail.com, joelaf@google.com,
	mathieu.desnoyers@efficios.com, baohong.liu@intel.com,
	rajvi.jingar@intel.com, julia@ni.com, fengguang.wu@intel.com,
	linux-kernel@vger.kernel.org, linux-rt-users@vger.kernel.org,
	Tom Zanussi <tom.zanussi@linux.intel.com>
Subject: [PATCH v7 27/37] tracing: Add 'onmax' hist trigger action support
Date: Wed,  6 Dec 2017 16:38:08 -0600	[thread overview]
Message-ID: <1a6f0f22c4c76aff3a91e366e9601791452b667d.1512593081.git.tom.zanussi@linux.intel.com> (raw)
In-Reply-To: <cover.1512593081.git.tom.zanussi@linux.intel.com>
In-Reply-To: <cover.1512593081.git.tom.zanussi@linux.intel.com>

Add an 'onmax(var).save(field,...)' hist trigger action which is
invoked whenever an event exceeds the current maximum.

The end result is that the trace event fields or variables specified
as the onmax.save() params will be saved if 'var' exceeds the current
maximum for that hist trigger entry.  This allows context from the
event that exhibited the new maximum to be saved for later reference.
When the histogram is displayed, additional fields displaying the
saved values will be printed.

As an example the below defines a couple of hist triggers, one for
sched_wakeup and another for sched_switch, keyed on pid.  Whenever a
sched_wakeup occurs, the timestamp is saved in the entry corresponding
to the current pid, and when the scheduler switches back to that pid,
the timestamp difference is calculated.  If the resulting latency
exceeds the current maximum latency, the specified save() values are
saved:

    # echo 'hist:keys=pid:ts0=common_timestamp.usecs \
        if comm=="cyclictest"' >> \
      /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger

    # echo 'hist:keys=next_pid:\
      wakeup_lat=common_timestamp.usecs-$ts0:\
      onmax($wakeup_lat).save(next_comm,prev_pid,prev_prio,prev_comm) \
        if next_comm=="cyclictest"' >> \
      /sys/kernel/debug/tracing/events/sched/sched_switch/trigger

When the histogram is displayed, the max value and the saved values
corresponding to the max are displayed following the rest of the
fields:

    # cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist

      { next_pid:       3728 } hitcount:        199 \
        max:        123  next_comm: cyclictest  prev_pid:          0 \
	prev_prio:        120  prev_comm: swapper/3
      { next_pid:       3730 } hitcount:       1321 \
        max:         15  next_comm: cyclictest  prev_pid:          0 \
	prev_prio:        120  prev_comm: swapper/1
      { next_pid:       3729 } hitcount:       1973\
        max:         25  next_comm: cyclictest  prev_pid:          0  \
	prev_prio:        120  prev_comm: swapper/0

    Totals:
        Hits: 3493
        Entries: 3
	Dropped: 0

Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
 kernel/trace/trace_events_hist.c | 331 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 296 insertions(+), 35 deletions(-)

diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 5df14c2..03114fb 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -292,6 +292,10 @@ struct hist_trigger_data {
 	unsigned int			n_field_var_str;
 	struct field_var_hist		*field_var_hists[SYNTH_FIELDS_MAX];
 	unsigned int			n_field_var_hists;
+
+	struct field_var		*max_vars[SYNTH_FIELDS_MAX];
+	unsigned int			n_max_vars;
+	unsigned int			n_max_var_str;
 };
 
 struct synth_field {
@@ -334,6 +338,14 @@ struct action_data {
 			char			*synth_event_name;
 			struct synth_event	*synth_event;
 		} onmatch;
+
+		struct {
+			char			*var_str;
+			char			*fn_name;
+			unsigned int		max_var_ref_idx;
+			struct hist_field	*max_var;
+			struct hist_field	*var;
+		} onmax;
 	};
 };
 
@@ -1714,7 +1726,8 @@ static int parse_action(char *str, struct hist_trigger_attrs *attrs)
 	if (attrs->n_actions >= HIST_ACTIONS_MAX)
 		return ret;
 
-	if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0)) {
+	if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0) ||
+	    (strncmp(str, "onmax(", strlen("onmax(")) == 0)) {
 		attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL);
 		if (!attrs->action_str[attrs->n_actions]) {
 			ret = -ENOMEM;
@@ -1886,7 +1899,7 @@ static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt)
 		}
 	}
 
-	n_str = hist_data->n_field_var_str;
+	n_str = hist_data->n_field_var_str + hist_data->n_max_var_str;
 
 	size = STR_VAR_LEN_MAX;
 
@@ -2907,6 +2920,15 @@ static void update_field_vars(struct hist_trigger_data *hist_data,
 			    hist_data->n_field_vars, 0);
 }
 
+static void update_max_vars(struct hist_trigger_data *hist_data,
+			    struct tracing_map_elt *elt,
+			    struct ring_buffer_event *rbe,
+			    void *rec)
+{
+	__update_field_vars(elt, rbe, rec, hist_data->max_vars,
+			    hist_data->n_max_vars, hist_data->n_field_var_str);
+}
+
 static struct hist_field *create_var(struct hist_trigger_data *hist_data,
 				     struct trace_event_file *file,
 				     char *name, int size, const char *type)
@@ -3036,6 +3058,227 @@ static struct field_var *create_field_var(struct hist_trigger_data *hist_data,
 	return create_field_var(target_hist_data, file, var_name);
 }
 
+static void onmax_print(struct seq_file *m,
+			struct hist_trigger_data *hist_data,
+			struct tracing_map_elt *elt,
+			struct action_data *data)
+{
+	unsigned int i, save_var_idx, max_idx = data->onmax.max_var->var.idx;
+
+	seq_printf(m, "\n\tmax: %10llu", tracing_map_read_var(elt, max_idx));
+
+	for (i = 0; i < hist_data->n_max_vars; i++) {
+		struct hist_field *save_val = hist_data->max_vars[i]->val;
+		struct hist_field *save_var = hist_data->max_vars[i]->var;
+		u64 val;
+
+		save_var_idx = save_var->var.idx;
+
+		val = tracing_map_read_var(elt, save_var_idx);
+
+		if (save_val->flags & HIST_FIELD_FL_STRING) {
+			seq_printf(m, "  %s: %-32s", save_var->var.name,
+				   (char *)(uintptr_t)(val));
+		} else
+			seq_printf(m, "  %s: %10llu", save_var->var.name, val);
+	}
+}
+
+static void onmax_save(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)
+{
+	unsigned int max_idx = data->onmax.max_var->var.idx;
+	unsigned int max_var_ref_idx = data->onmax.max_var_ref_idx;
+
+	u64 var_val, max_val;
+
+	var_val = var_ref_vals[max_var_ref_idx];
+	max_val = tracing_map_read_var(elt, max_idx);
+
+	if (var_val <= max_val)
+		return;
+
+	tracing_map_set_var(elt, max_idx, var_val);
+
+	update_max_vars(hist_data, elt, rbe, rec);
+}
+
+static void onmax_destroy(struct action_data *data)
+{
+	unsigned int i;
+
+	destroy_hist_field(data->onmax.max_var, 0);
+	destroy_hist_field(data->onmax.var, 0);
+
+	kfree(data->onmax.var_str);
+	kfree(data->onmax.fn_name);
+
+	for (i = 0; i < data->n_params; i++)
+		kfree(data->params[i]);
+
+	kfree(data);
+}
+
+static int onmax_create(struct hist_trigger_data *hist_data,
+			struct action_data *data)
+{
+	struct trace_event_file *file = hist_data->event_file;
+	struct hist_field *var_field, *ref_field, *max_var;
+	unsigned int var_ref_idx = hist_data->n_var_refs;
+	struct field_var *field_var;
+	char *onmax_var_str, *param;
+	unsigned long flags;
+	unsigned int i;
+	int ret = 0;
+
+	onmax_var_str = data->onmax.var_str;
+	if (onmax_var_str[0] != '$')
+		return -EINVAL;
+	onmax_var_str++;
+
+	var_field = find_target_event_var(hist_data, NULL, NULL, onmax_var_str);
+	if (!var_field)
+		return -EINVAL;
+
+	flags = HIST_FIELD_FL_VAR_REF;
+	ref_field = create_hist_field(hist_data, NULL, flags, NULL);
+	if (!ref_field)
+		return -ENOMEM;
+
+	if (init_var_ref(ref_field, var_field, NULL, NULL)) {
+		destroy_hist_field(ref_field, 0);
+		ret = -ENOMEM;
+		goto out;
+	}
+	hist_data->var_refs[hist_data->n_var_refs] = ref_field;
+	ref_field->var_ref_idx = hist_data->n_var_refs++;
+	data->onmax.var = ref_field;
+
+	data->fn = onmax_save;
+	data->onmax.max_var_ref_idx = var_ref_idx;
+	max_var = create_var(hist_data, file, "max", sizeof(u64), "u64");
+	if (IS_ERR(max_var)) {
+		ret = PTR_ERR(max_var);
+		goto out;
+	}
+	data->onmax.max_var = max_var;
+
+	for (i = 0; i < data->n_params; i++) {
+		param = kstrdup(data->params[i], GFP_KERNEL);
+		if (!param) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		field_var = create_target_field_var(hist_data, NULL, NULL, param);
+		if (IS_ERR(field_var)) {
+			ret = PTR_ERR(field_var);
+			kfree(param);
+			goto out;
+		}
+
+		hist_data->max_vars[hist_data->n_max_vars++] = field_var;
+		if (field_var->val->flags & HIST_FIELD_FL_STRING)
+			hist_data->n_max_var_str++;
+
+		kfree(param);
+	}
+ out:
+	return ret;
+}
+
+static int parse_action_params(char *params, struct action_data *data)
+{
+	char *param, *saved_param;
+	int ret = 0;
+
+	while (params) {
+		if (data->n_params >= SYNTH_FIELDS_MAX)
+			goto out;
+
+		param = strsep(&params, ",");
+		if (!param) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		param = strstrip(param);
+		if (strlen(param) < 2) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		saved_param = kstrdup(param, GFP_KERNEL);
+		if (!saved_param) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		data->params[data->n_params++] = saved_param;
+	}
+ out:
+	return ret;
+}
+
+static struct action_data *onmax_parse(char *str)
+{
+	char *onmax_fn_name, *onmax_var_str;
+	struct action_data *data;
+	int ret = -EINVAL;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return ERR_PTR(-ENOMEM);
+
+	onmax_var_str = strsep(&str, ")");
+	if (!onmax_var_str || !str) {
+		ret = -EINVAL;
+		goto free;
+	}
+
+	data->onmax.var_str = kstrdup(onmax_var_str, GFP_KERNEL);
+	if (!data->onmax.var_str) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	strsep(&str, ".");
+	if (!str)
+		goto free;
+
+	onmax_fn_name = strsep(&str, "(");
+	if (!onmax_fn_name || !str)
+		goto free;
+
+	if (strncmp(onmax_fn_name, "save", strlen("save")) == 0) {
+		char *params = strsep(&str, ")");
+
+		if (!params) {
+			ret = -EINVAL;
+			goto free;
+		}
+
+		ret = parse_action_params(params, data);
+		if (ret)
+			goto free;
+	} else
+		goto free;
+
+	data->onmax.fn_name = kstrdup(onmax_fn_name, GFP_KERNEL);
+	if (!data->onmax.fn_name) {
+		ret = -ENOMEM;
+		goto free;
+	}
+ out:
+	return data;
+ free:
+	onmax_destroy(data);
+	data = ERR_PTR(ret);
+	goto out;
+}
+
 static void onmatch_destroy(struct action_data *data)
 {
 	unsigned int i;
@@ -3120,39 +3363,6 @@ static int check_synth_field(struct synth_event *event,
 	return 0;
 }
 
-static int parse_action_params(char *params, struct action_data *data)
-{
-	char *param, *saved_param;
-	int ret = 0;
-
-	while (params) {
-		if (data->n_params >= SYNTH_FIELDS_MAX)
-			goto out;
-
-		param = strsep(&params, ",");
-		if (!param) {
-			ret = -EINVAL;
-			goto out;
-		}
-
-		param = strstrip(param);
-		if (strlen(param) < 2) {
-			ret = -EINVAL;
-			goto out;
-		}
-
-		saved_param = kstrdup(param, GFP_KERNEL);
-		if (!saved_param) {
-			ret = -ENOMEM;
-			goto out;
-		}
-
-		data->params[data->n_params++] = saved_param;
-	}
- out:
-	return ret;
-}
-
 static struct hist_field *
 onmatch_find_var(struct hist_trigger_data *hist_data, struct action_data *data,
 		 char *system, char *event, char *var)
@@ -3809,6 +4019,8 @@ static void destroy_actions(struct hist_trigger_data *hist_data)
 
 		if (data->fn == action_trace)
 			onmatch_destroy(data);
+		else if (data->fn == onmax_save)
+			onmax_destroy(data);
 		else
 			kfree(data);
 	}
@@ -3834,6 +4046,15 @@ static int parse_actions(struct hist_trigger_data *hist_data)
 				break;
 			}
 			data->fn = action_trace;
+		} else if (strncmp(str, "onmax(", strlen("onmax(")) == 0) {
+			char *action_str = str + strlen("onmax(");
+
+			data = onmax_parse(action_str);
+			if (IS_ERR(data)) {
+				ret = PTR_ERR(data);
+				break;
+			}
+			data->fn = onmax_save;
 		} else {
 			ret = -EINVAL;
 			break;
@@ -3859,12 +4080,48 @@ static int create_actions(struct hist_trigger_data *hist_data,
 			ret = onmatch_create(hist_data, file, data);
 			if (ret)
 				return ret;
+		} else if (data->fn == onmax_save) {
+			ret = onmax_create(hist_data, data);
+			if (ret)
+				return ret;
 		}
 	}
 
 	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];
+
+		if (data->fn == onmax_save)
+			onmax_print(m, hist_data, elt, data);
+	}
+}
+
+static void print_onmax_spec(struct seq_file *m,
+			     struct hist_trigger_data *hist_data,
+			     struct action_data *data)
+{
+	unsigned int i;
+
+	seq_puts(m, ":onmax(");
+	seq_printf(m, "%s", data->onmax.var_str);
+	seq_printf(m, ").%s(", data->onmax.fn_name);
+
+	for (i = 0; i < hist_data->n_max_vars; i++) {
+		seq_printf(m, "%s", hist_data->max_vars[i]->var->var.name);
+		if (i < hist_data->n_max_vars - 1)
+			seq_puts(m, ",");
+	}
+	seq_puts(m, ")");
+}
+
 static void print_onmatch_spec(struct seq_file *m,
 			       struct hist_trigger_data *hist_data,
 			       struct action_data *data)
@@ -3895,6 +4152,8 @@ static void print_actions_spec(struct seq_file *m,
 
 		if (data->fn == action_trace)
 			print_onmatch_spec(m, hist_data, data);
+		else if (data->fn == onmax_save)
+			print_onmax_spec(m, hist_data, data);
 	}
 }
 
@@ -4276,6 +4535,8 @@ static void hist_trigger_stacktrace_print(struct seq_file *m,
 		}
 	}
 
+	print_actions(m, hist_data, elt);
+
 	seq_puts(m, "\n");
 }
 
-- 
1.9.3

  parent reply	other threads:[~2017-12-06 22:40 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-06 22:37 [PATCH v7 00/37] tracing: Inter-event (e.g. latency) support Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 01/37] tracing: Move hist trigger Documentation to histogram.txt Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 02/37] tracing: Add Documentation for log2 modifier Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 03/37] tracing: Add support to detect and avoid duplicates Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 04/37] tracing: Remove code which merges duplicates Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 05/37] ring-buffer: Add interface for setting absolute time stamps Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 06/37] ring-buffer: Redefine the unimplemented RINGBUF_TYPE_TIME_STAMP Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 07/37] tracing: Add timestamp_mode trace file Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 08/37] tracing: Give event triggers access to ring_buffer_event Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 09/37] tracing: Add ring buffer event param to hist field functions Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 10/37] tracing: Break out hist trigger assignment parsing Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 11/37] tracing: Add hist trigger timestamp support Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 12/37] tracing: Add per-element variable support to tracing_map Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 13/37] tracing: Add hist_data member to hist_field Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 14/37] tracing: Add usecs modifier for hist trigger timestamps Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 15/37] tracing: Add variable support to hist triggers Tom Zanussi
2017-12-08 13:02   ` Namhyung Kim
2017-12-08 19:54     ` Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 16/37] tracing: Account for variables in named trigger compatibility Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 17/37] tracing: Move get_hist_field_flags() Tom Zanussi
2017-12-06 22:37 ` [PATCH v7 18/37] tracing: Add simple expression support to hist triggers Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 19/37] tracing: Generalize per-element hist trigger data Tom Zanussi
2017-12-08 13:06   ` Namhyung Kim
2017-12-08 19:56     ` Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 20/37] tracing: Pass tracing_map_elt to hist_field accessor functions Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 21/37] tracing: Add hist_field 'type' field Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 22/37] tracing: Add variable reference handling to hist triggers Tom Zanussi
2017-12-11 15:17   ` Namhyung Kim
2017-12-11 17:53     ` Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 23/37] tracing: Add hist trigger action hook Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 24/37] tracing: Add support for 'synthetic' events Tom Zanussi
2017-12-12 14:42   ` Namhyung Kim
2017-12-12 16:07     ` Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 25/37] tracing: Add support for 'field variables' Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 26/37] tracing: Add 'onmatch' hist trigger action support Tom Zanussi
2017-12-06 22:38 ` Tom Zanussi [this message]
2017-12-06 22:38 ` [PATCH v7 28/37] tracing: Allow whitespace to surround hist trigger filter Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 29/37] tracing: Add cpu field for hist triggers Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 30/37] tracing: Add hist trigger support for variable reference aliases Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 31/37] tracing: Add 'last error' error facility for hist triggers Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 32/37] tracing: Add inter-event hist trigger Documentation Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 33/37] tracing: Make tracing_set_clock() non-static Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 34/37] tracing: Add a clock attribute for hist triggers Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 35/37] tracing: Increase trace_recursive_lock() limit for synthetic events Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 36/37] tracing: Add inter-event blurb to HIST_TRIGGERS config option Tom Zanussi
2017-12-06 22:38 ` [PATCH v7 37/37] selftests: ftrace: Add inter-event hist triggers testcases Tom Zanussi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1a6f0f22c4c76aff3a91e366e9601791452b667d.1512593081.git.tom.zanussi@linux.intel.com \
    --to=tom.zanussi@linux.intel.com \
    --cc=baohong.liu@intel.com \
    --cc=bigeasy@linutronix.de \
    --cc=fengguang.wu@intel.com \
    --cc=joel.opensrc@gmail.com \
    --cc=joelaf@google.com \
    --cc=julia@ni.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rt-users@vger.kernel.org \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=mhiramat@kernel.org \
    --cc=namhyung@kernel.org \
    --cc=rajvi.jingar@intel.com \
    --cc=rostedt@goodmis.org \
    --cc=tglx@linutronix.de \
    --cc=vedang.patel@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).