Skip to contentSkip to conversation

A few hist trigger fixes

PATCH New Tom Zanussi 1 participant 4 messages Apr 18, 2019 Tracing

lore
Share & tools
lore
#0 lore raw

cover A few hist trigger fixes

From: Tom Zanussi <[email protected]>

Hi Steve,

Here's a fix for a problem I found with the hist trigger snapshot action related to the refactoring in the later versions of the patchset ('[PATCH 3/3] tracing: Add a check_val() check before updating cond_snapshot() track_val').

I also added v2 of 2 other patches that I previously submitted [1] but realized hadn't gotten picked up. ('[PATCH v2 1/3] tracing: Prevent hist_field_var_ref() from accessing NULL tracing_map_elts' and '[PATCH v2 2/3] tracing: Check keys for variable references in expressions too').

One of them I had to rebase ('[PATCH v2 2/3] tracing: Check keys for variable references in expressions too') because of the error_log changes since then, but the original ones still apply to versions before that).

So in summary, all of these patches apply to current ftrace/core, while only '[PATCH 3/3] tracing: Add a check_val() check before updating cond_snapshot() track_val' and the previous 2 original patches in [1] apply to mainline.

Thanks,

Tom

[1] https://lore.kernel.org/lkml/[email protected]/

The following changes since commit c8faaa4c594f55ddf903d61180029a3ea1da5286:

rcu: validate arguments for rcu tracepoints (2019-04-03 08:26:38 -0400)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/zanussi/linux-trace.git ftrace/key-ref-fix-v2

Tom Zanussi (3):
tracing: Prevent hist_field_var_ref() from accessing NULL
tracing_map_elts
tracing: Check keys for variable references in expressions too
tracing: Add a check_val() check before updating cond_snapshot()
track_val

 kernel/trace/trace_events_hist.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
Signature · 1 line
2.14.1
#1 lore raw

PATCH v2 1/3 tracing: Prevent hist_field_var_ref() from accessing NULL tracing_map_elts

From: Tom Zanussi <[email protected]>

hist_field_var_ref() is an implementation of hist_field_fn_t(), which can be called with a null tracing_map_elt elt param when assembling a key in event_hist_trigger().

In the case of hist_field_var_ref() this doesn't make sense, because a variable can only be resolved by looking it up using an already assembled key i.e. a variable can't be used to assemble a key since the key is required in order to access the variable.

Upper layers should prevent the user from constructing a key using a variable in the first place, but in case one slips through, it shouldn't cause a NULL pointer dereference. Also if one does slip through, we want to know about it, so emit a one-time warning in that case.

---
 kernel/trace/trace_events_hist.c | 3 +++
 1 file changed, 3 insertions(+)
kernel/trace/trace_events_hist.c +3 -0
index a1136e043f17..453f51ef83ca 100644
@@ -1854,6 +1854,9 @@ static u64 hist_field_var_ref(struct hist_field *hist_field,
 	struct hist_elt_data *elt_data;
 	u64 var_val = 0;
 
+	if (WARN_ON_ONCE(!elt))
+		return var_val;
+
 	elt_data = elt->private_data;
 	var_val = elt_data->var_ref_vals[hist_field->var_ref_idx];
 
Signature · 1 line
2.14.1
#2 lore raw

PATCH v2 2/3 tracing: Check keys for variable references in expressions too

In reply to Tom Zanussi

From: Tom Zanussi <[email protected]>

There's an existing check for variable references in keys, but it doesn't go far enough. It checks whether a key field is a variable reference but doesn't check whether it's an expression containing variable references, which can cause the same problems for callers.

Use the existing field_has_hist_vars() function rather than a direct top-level flag check to catch all possible variable references.

---
 kernel/trace/trace_events_hist.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/trace/trace_events_hist.c +2 -2
index 453f51ef83ca..cb56c7c456fa 100644
@@ -59,7 +59,7 @@
 	C(NO_CLOSING_PAREN,	"No closing paren found"),		\
 	C(SUBSYS_NOT_FOUND,	"Missing subsystem"),			\
 	C(INVALID_SUBSYS_EVENT,	"Invalid subsystem or event name"),	\
-	C(INVALID_REF_KEY,	"Using variable references as keys not supported"), \
+	C(INVALID_REF_KEY,	"Using variable references in keys not supported"), \
 	C(VAR_NOT_FOUND,	"Couldn't find variable"),		\
 	C(FIELD_NOT_FOUND,	"Couldn't find field"),
 
@@ -4506,7 +4506,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
 			goto out;
 		}
 
-		if (hist_field->flags & HIST_FIELD_FL_VAR_REF) {
+		if (field_has_hist_vars(hist_field, 0))	{
 			hist_err(tr, HIST_ERR_INVALID_REF_KEY, errpos(field_str));
 			destroy_hist_field(hist_field, 0);
 			ret = -EINVAL;
Signature · 1 line
2.14.1
#3 lore raw

PATCH 3/3 tracing: Add a check_val() check before updating cond_snapshot() track_val

In reply to Tom Zanussi

From: Tom Zanussi <[email protected]>

Without this check a snapshot is taken whenever a bucket's max is hit, rather than only when the global max is hit, as it should be.

Before:

In this example, we do a first run of the workload (cyclictest), examine the output, note the max ('triggering value') (347), then do a second run and note the max again.

In this case, the max in the second run (39) is below the max in the first run, but since we haven't cleared the histogram, the first max is still in the histogram and is higher than any other max, so it should still be the max for the snapshot. It isn't however - the value should still be 347 after the second run.

  # echo 'hist:keys=pid:ts0=common_timestamp.usecs if comm=="cyclictest"' >> /sys/kernel/debug/tracing/events/sched/sched_waking/trigger
  # echo 'hist:keys=next_pid:wakeup_lat=common_timestamp.usecs-$ts0:onmax($wakeup_lat).save(next_prio,next_comm,prev_pid,prev_prio,prev_comm):onmax($wakeup_lat).snapshot() if next_comm=="cyclictest"' >> /sys/kernel/debug/tracing/events/sched/sched_switch/trigger
  # cyclictest -p 80 -n -s -t 2 -D 2
  # cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist
  { next_pid:       2143 } hitcount:        199
    max:         44  next_prio:        120  next_comm: cyclictest
    prev_pid:          0  prev_prio:        120  prev_comm: swapper/4

{ next_pid: 2145 } hitcount: 1325
max: 38 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/2

{ next_pid: 2144 } hitcount: 1982
max: 347 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/6

Snapshot taken (see tracing/snapshot). Details:
triggering value { onmax($wakeup_lat) }: 347
triggered by event with key: { next_pid: 2144 }

  # cyclictest -p 80 -n -s -t 2 -D 2
  # cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist
  { next_pid:       2143 } hitcount:        199
    max:         44  next_prio:        120  next_comm: cyclictest
    prev_pid:          0  prev_prio:        120  prev_comm: swapper/4
  { next_pid:       2148 } hitcount:        199
    max:         16  next_prio:        120  next_comm: cyclictest
    prev_pid:          0  prev_prio:        120  prev_comm: swapper/1

{ next_pid: 2145 } hitcount: 1325
max: 38 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/2

{ next_pid: 2150 } hitcount: 1326
max: 39 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/4

{ next_pid: 2144 } hitcount: 1982
max: 347 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/6

{ next_pid: 2149 } hitcount: 1983
max: 130 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/0

Snapshot taken (see tracing/snapshot). Details:
triggering value { onmax($wakeup_lat) }: 39
triggered by event with key: { next_pid: 2150 }

After:

In this example, we do a first run of the workload (cyclictest), examine the output, note the max ('triggering value') (375), then do a second run and note the max again.

In this case, the max in the second run is still 375, the highest in any bucket, as it should be.

  # cyclictest -p 80 -n -s -t 2 -D 2
  # cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist
  { next_pid:       2072 } hitcount:        200
    max:         28  next_prio:        120  next_comm: cyclictest
    prev_pid:          0  prev_prio:        120  prev_comm: swapper/5

{ next_pid: 2074 } hitcount: 1323
max: 375 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/2

{ next_pid: 2073 } hitcount: 1980
max: 153 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/6

Snapshot taken (see tracing/snapshot). Details:
triggering value { onmax($wakeup_lat) }: 375
triggered by event with key: { next_pid: 2074 }

  # cyclictest -p 80 -n -s -t 2 -D 2
  # cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist
  { next_pid:       2101 } hitcount:        199
    max:         49  next_prio:        120  next_comm: cyclictest
    prev_pid:          0  prev_prio:        120  prev_comm: swapper/6
  { next_pid:       2072 } hitcount:        200
    max:         28  next_prio:        120  next_comm: cyclictest
    prev_pid:          0  prev_prio:        120  prev_comm: swapper/5

{ next_pid: 2074 } hitcount: 1323
max: 375 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/2

{ next_pid: 2103 } hitcount: 1325
max: 74 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/4

{ next_pid: 2073 } hitcount: 1980
max: 153 next_prio: 19 next_comm: cyclictest
prev_pid: 0 prev_prio: 120 prev_comm: swapper/6

{ next_pid: 2102 } hitcount: 1981
max: 84 next_prio: 19 next_comm: cyclictest
prev_pid: 12 prev_prio: 120 prev_comm: kworker/0:1

Snapshot taken (see tracing/snapshot). Details:
triggering value { onmax($wakeup_lat) }: 375
triggered by event with key: { next_pid: 2074 }

---
 kernel/trace/trace_events_hist.c | 6 ++++++
 1 file changed, 6 insertions(+)
kernel/trace/trace_events_hist.c +6 -0
index cb56c7c456fa..61b58c906489 100644
@@ -3585,14 +3585,20 @@ static bool cond_snapshot_update(struct trace_array *tr, void *cond_data)
 	struct track_data *track_data = tr->cond_snapshot->cond_data;
 	struct hist_elt_data *elt_data, *track_elt_data;
 	struct snapshot_context *context = cond_data;
+	struct action_data *action;
 	u64 track_val;
 
 	if (!track_data)
 		return false;
 
+	action = track_data->action_data;
+
 	track_val = get_track_val(track_data->hist_data, context->elt,
 				  track_data->action_data);
 
+	if (!action->track_data.check_val(track_data->track_val, track_val))
+		return false;
+
 	track_data->track_val = track_val;
 	memcpy(track_data->key, context->key, track_data->key_len);
 
Signature · 1 line
2.14.1