Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1675146 > unrolled thread
| Started by | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| First post | 2017-06-27 00:50 +0200 |
| Last post | 2017-07-01 09:10 +0200 |
| Articles | 20 on this page of 38 — 4 participants |
Back to article view | Back to linux.kernel
[PATCH 00/32] tracing: Inter-event (e.g. latency) support Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 00:50 +0200
[PATCH 17/32] tracing: Account for variables in named trigger compatibility Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 02/32] tracing: Reimplement log2 Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 10/32] tracing: Add NO_DISCARD event file flag Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 11/32] tracing: Add post-trigger flag to hist trigger command Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 19/32] tracing: Add variable reference handling to hist triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 29/32] tracing: Add 'last error' error facility for hist triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 04/32] ring-buffer: Redefine the unimplemented RINGBUF_TIME_TIME_STAMP Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
Re: [PATCH 04/32] ring-buffer: Redefine the unimplemented RINGBUF_TIME_TIME_STAMP "Joel Fernandes (Google)" <joel.opensrc@gmail.com> - 2017-07-02 11:00 +0200
[PATCH 06/32] tracing: Add ring buffer event param to hist field functions Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 16/32] tracing: Add variable support to hist triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 20/32] tracing: Add support for dynamic tracepoints Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 24/32] tracing: Add 'onmax' hist trigger action support Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 03/32] ring-buffer: Add interface for setting absolute time stamps Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 13/32] tracing: Add per-element variable support to tracing_map Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 09/32] tracing: Make traceprobe parsing code reusable Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 28/32] tracing: Add hist trigger support for variable reference aliases Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 32/32] tracing: Add a clock attribute for hist triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 25/32] tracing: Allow whitespace to surround hist trigger filter Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 05/32] tracing: Give event triggers access to ring_buffer_event Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 21/32] tracing: Add hist trigger action hook Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 07/32] tracing: Increase tracing map KEYS_MAX size Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 30/32] tracing: Add inter-event hist trigger Documentation Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 31/32] tracing: Make tracing_set_clock() non-static Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 22/32] tracing: Add support for 'synthetic' events Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 12/32] tracing: Add hist trigger timestamp support Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 18/32] tracing: Add simple expression support to hist triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 08/32] tracing: Break out hist trigger assignment parsing Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 15/32] tracing: Add usecs modifier for hist trigger timestamps Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 23/32] tracing: Add 'onmatch' hist trigger action support Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 14/32] tracing: Add hist_data member to hist_field Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 26/32] tracing: Make duplicate count from tracing_map available Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 01/32] tracing: Add hist_field_name() accessor Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
[PATCH 27/32] tracing: Add cpu field for hist triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-27 01:00 +0200
Re: [PATCH 00/32] tracing: Inter-event (e.g. latency) support Steven Rostedt <rostedt@goodmis.org> - 2017-06-27 17:10 +0200
Re: [PATCH 00/32] tracing: Inter-event (e.g. latency) support Masami Hiramatsu <mhiramat@kernel.org> - 2017-06-28 16:30 +0200
Re: [PATCH 00/32] tracing: Inter-event (e.g. latency) support Tom Zanussi <tom.zanussi@linux.intel.com> - 2017-06-28 21:20 +0200
Re: [PATCH 00/32] tracing: Inter-event (e.g. latency) support "Joel Fernandes (Google)" <joel.opensrc@gmail.com> - 2017-07-01 09:10 +0200
Page 1 of 2 [1] 2 Next page →
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 00:50 +0200 |
| Subject | [PATCH 00/32] tracing: Inter-event (e.g. latency) support |
| Message-ID | <tWKZs-8nP-19@gated-at.bofh.it> |
This patchset adds support for 'inter-event' quantities to the trace
event subsystem. The most important example of inter-event quantities
are latencies, or the time differences between two events.
This is a follow-up to the initial RFC patchset and subsequent
discussion at ELC. I think I've covered all the basic suggestions
from both those sources - thanks to everyone who reviewed and
contributed suggestions. Also, thanks to Vedang Patel for testing and
providing real-user input and scripts - a number of the new patches
such as extended error reporting, cpu fields, and aliases address his
input directly.
The current patchset has been fairly well tested and I'm not aware of
any major problems with it, but there is one problem/feature to be
aware of, which has to do with the fact that currently the tracing map
can naturally have duplicate entries. Normally this is expected and
isn't a problem for histograms without variables attached, as
duplicates are merged on output. If a histogram has variables though
and gets a duplicate entry, the variable associated with that record
can't be trusted, since it can't be retrieved again. Luckily
duplicates are normally very rare occurrences - I've never personally
seen one in practice - but it does mean that you always need to check
that the 'Duplicates' value on the histogram summary is 0 if that
histogram has variables attached. We're currently working on a 'fix'
for this that basically eliminates the possibility of duplicates, but
for now the 'Duplicates' total will alert users to that rare situation
should it occur.
One of the main motivations for adding this capability is to provide a
general-purpose base that existing existing tools such as the -RT
latency_hist patchset can be built upon, while at the same time
providing a simple way for users to track latencies (or any
inter-event quantity) generically between any two events.
Previous -RT latency_hist patchsets that take advantage of the trace
event subsystem have been submitted, but they essentially hard-code
special-case tracepoints and application logic in ways that can't be
reused. It seemed to me that rather than providing a one-off patchset
devoted specifically to generating the specific histograms in the
latency_hist patchset, it should be possible to build the same
functionality on top of a generic layer allowing users to do similar
things for other non-latency_hist applications.
In addition to preliminary patches that add some basic missing
functionality such as a common ringbuffer-derived timestamp and
dynamically-creatable tracepoints, the overall patchset is divided up
into a few different areas that combine to produce the overall goal
(The Documentation patch explains all the details):
- variables and simple expressions required to calculate a latency
In order to calculate a latency or any inter-event value,
something from one event needs to be saved and later retrieved,
and some operation such as subtraction or addition is performed on
it. This means some minimal form of variables and expressions,
which the first set of patches implements. Saving and retrieving
events to use in a latency calculation is normally done using a
hash table, and that's exactly what we have with trace event hist
triggers, so that's where variables are instantiated, set, and
retrieved. Basically, variables are set on one entry and
retrieved and used by a 'matching' event.
- 'synthetic' events, combining variables from other events
The trace event interface is based on pseudo-files associated with
individual events, so it wouldn't really make sense to have
quantities derived from multiple events attached to any one of
those events. For that reason, the patchset implements a means of
combining variables from other events into a separate 'synthetic'
event, which can be treated as if it were just like any other
trace event in the system.
- 'actions' generating synthetic events, among other things
Variables and synthetic events provide the data and data structure
for new events, but something still needs to actually generate an
event using that data. 'Actions' are expanded to provide that
capability. Though it hasn't been explicitly called as much
before, the default 'action' currently for a hist trigger is to
update the matching histogram entry's sum values. This patchset
essentially expands that to provide a new 'onmatch.event(params)'
action that can be used to have one event generate another. The
mechanism is extensible to other actions, and in fact the patchset
also includes another, 'onmax(var).save(field,...)' that can be
used to save context whenever a value exceeds the previous maximum
(something also needed by latency_hist).
Here are some examples that should make things less abstract.
====
Example - wakeup latency
====
This basically implements the -RT latency_hist 'wakeup_latency'
histogram using the synthetic events, variables, and actions
described. The output below is from a run of cyclictest using the
following command:
# rt-tests/cyclictest -p 80 -n -s -t 2
What we're measuring the latency of is the time between when a
thread (of cyclictest) is awakened and when it's scheduled in. To
do that we add triggers to sched_wakeup and sched_switch with the
appropriate variables, and on a matching sched_switch event,
generate a synthetic 'wakeup_latency' event. Since it's just
another trace event like any other, we can also define a histogram
on that event, the output of which is what we see displayed when
reading the wakeup_latency 'hist' file.
First, we create a synthetic event called wakeup_latency, that
creates 3 fields which will reference variables from other events:
# echo 'wakeup_latency u64 lat; pid_t pid; int prio' >> \
/sys/kernel/debug/tracing/synthetic_events
Next we add a trigger to sched_wakeup, which saves the value of the
'$common_timestamp' when that event is hit in a variable, ts0. Note
that this happens only when 'comm==cyclictest'.
Also, '$common_timestamp' is a new field defined on every event (if
needed - if there are no users of timestamps in a trace, timestamps
won't be saved and there's no additional overhead from that).
# echo 'hist:keys=pid:ts0=$common_timestamp.usecs if comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
Next, we add a trigger to sched_switch. When the pid being switched
to matches the pid woken up by a previous sched_wakeup event, this
event grabs the ts0 variable saved on that event, takes the
difference between it and the current sched_switch's
$common_timestamp, and assigns it to a new 'wakeup_lat' variable.
It then generates the wakeup_latency synthetic event defined earlier
by 'invoking' it as a function using as parameters the wakeup_lat
variable and two sched_switch event fields directly:
# echo 'hist:keys=next_pid:wakeup_lat=$common_timestamp.usecs-$ts0: \
onmatch(sched.sched_wakeup).wakeup_latency($wakeup_lat,next_pid,next_prio) \
if next_comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_switch/trigger
Finally, all we have left to do is create a standard histogram
simply naming the fields of the wakeup_latency synthetic event:
# echo 'hist:keys=pid,prio,lat:sort=pid,lat' >> \
/sys/kernel/debug/tracing/events/synthetic/wakeup_latency/trigger
At any time, we can see the histogram output by simply reading the
synthetic/wakeup_latency/hist file:
# cat /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/hist
# event histogram
#
# trigger info: hist:keys=pid,prio,lat:vals=hitcount:sort=pid,lat:size=2048 [active]
#
{ pid: 2519, prio: 120, lat: 1 } hitcount: 12
{ pid: 2519, prio: 120, lat: 2 } hitcount: 671
{ pid: 2519, prio: 120, lat: 3 } hitcount: 588
{ pid: 2519, prio: 120, lat: 4 } hitcount: 202
{ pid: 2519, prio: 120, lat: 5 } hitcount: 28
{ pid: 2519, prio: 120, lat: 6 } hitcount: 13
{ pid: 2519, prio: 120, lat: 7 } hitcount: 12
{ pid: 2519, prio: 120, lat: 8 } hitcount: 7
{ pid: 2519, prio: 120, lat: 9 } hitcount: 12
{ pid: 2519, prio: 120, lat: 10 } hitcount: 11
{ pid: 2519, prio: 120, lat: 11 } hitcount: 7
{ pid: 2519, prio: 120, lat: 12 } hitcount: 6
{ pid: 2519, prio: 120, lat: 13 } hitcount: 1
{ pid: 2519, prio: 120, lat: 17 } hitcount: 1
{ pid: 2519, prio: 120, lat: 18 } hitcount: 3
{ pid: 2519, prio: 120, lat: 19 } hitcount: 2
{ pid: 2519, prio: 120, lat: 22 } hitcount: 2
{ pid: 2519, prio: 120, lat: 23 } hitcount: 1
{ pid: 2519, prio: 120, lat: 24 } hitcount: 1
{ pid: 2519, prio: 120, lat: 27 } hitcount: 1
{ pid: 2519, prio: 120, lat: 34 } hitcount: 1
{ pid: 2519, prio: 120, lat: 53 } hitcount: 1
{ pid: 2519, prio: 120, lat: 67 } hitcount: 1
{ pid: 2519, prio: 120, lat: 69 } hitcount: 1
{ pid: 2521, prio: 19, lat: 1 } hitcount: 735
{ pid: 2521, prio: 19, lat: 2 } hitcount: 8978
{ pid: 2521, prio: 19, lat: 3 } hitcount: 4798
{ pid: 2521, prio: 19, lat: 4 } hitcount: 716
{ pid: 2521, prio: 19, lat: 5 } hitcount: 298
{ pid: 2521, prio: 19, lat: 6 } hitcount: 136
{ pid: 2521, prio: 19, lat: 7 } hitcount: 93
{ pid: 2521, prio: 19, lat: 8 } hitcount: 51
{ pid: 2521, prio: 19, lat: 9 } hitcount: 20
{ pid: 2521, prio: 19, lat: 10 } hitcount: 18
{ pid: 2521, prio: 19, lat: 11 } hitcount: 3
{ pid: 2521, prio: 19, lat: 12 } hitcount: 1
{ pid: 2521, prio: 19, lat: 13 } hitcount: 3
{ pid: 2521, prio: 19, lat: 15 } hitcount: 1
{ pid: 2521, prio: 19, lat: 17 } hitcount: 1
{ pid: 2521, prio: 19, lat: 18 } hitcount: 1
{ pid: 2521, prio: 19, lat: 20 } hitcount: 2
{ pid: 2521, prio: 19, lat: 22 } hitcount: 1
{ pid: 2521, prio: 19, lat: 25 } hitcount: 1
{ pid: 2521, prio: 19, lat: 26 } hitcount: 1
{ pid: 2522, prio: 19, lat: 1 } hitcount: 392
{ pid: 2522, prio: 19, lat: 2 } hitcount: 5376
{ pid: 2522, prio: 19, lat: 3 } hitcount: 3982
{ pid: 2522, prio: 19, lat: 4 } hitcount: 500
{ pid: 2522, prio: 19, lat: 5 } hitcount: 202
{ pid: 2522, prio: 19, lat: 6 } hitcount: 67
{ pid: 2522, prio: 19, lat: 7 } hitcount: 35
{ pid: 2522, prio: 19, lat: 8 } hitcount: 12
{ pid: 2522, prio: 19, lat: 9 } hitcount: 9
{ pid: 2522, prio: 19, lat: 10 } hitcount: 4
{ pid: 2522, prio: 19, lat: 11 } hitcount: 3
{ pid: 2522, prio: 19, lat: 12 } hitcount: 1
{ pid: 2522, prio: 19, lat: 13 } hitcount: 1
{ pid: 2522, prio: 19, lat: 16 } hitcount: 1
{ pid: 2522, prio: 19, lat: 18 } hitcount: 2
{ pid: 2522, prio: 19, lat: 19 } hitcount: 1
{ pid: 2522, prio: 19, lat: 21 } hitcount: 2
{ pid: 2522, prio: 19, lat: 22 } hitcount: 1
{ pid: 2522, prio: 19, lat: 23 } hitcount: 1
{ pid: 2522, prio: 19, lat: 45 } hitcount: 1
{ pid: 2522, prio: 19, lat: 82 } hitcount: 1
Totals:
Hits: 28037
Entries: 65
Dropped: 0
Duplicates: 0
The above output uses the .usecs modifier to common_timestamp, so
the latencies are reported in microseconds. The default, without
the modifier, is nanoseconds, but that's too fine-grained to put
directly into a histogram - for that however we can use the .log2
modifier on the 'lat' key. Otherwise the rest is the same:
# echo 'wakeup_latency u64 lat; pid_t pid; int prio' >> \
/sys/kernel/debug/tracing/synthetic_events
# echo 'hist:keys=pid:ts0=$common_timestamp if comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
# echo 'hist:keys=next_pid:wakeup_lat=$common_timestamp-$ts0: \
onmatch(sched.sched_wakeup).wakeup_latency($wakeup_lat,next_pid,next_prio) \
if next_comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_switch/trigger
# echo 'hist:keys=pid,prio,lat.log2:sort=pid,lat' >> \
/sys/kernel/debug/tracing/events/synthetic/wakeup_latency/trigger
# cat /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/hist
# event histogram
#
# trigger info: hist:keys=pid,prio,lat.log2:vals=hitcount:sort=pid,lat.log2:size=2048 [active]
#
{ pid: 2585, prio: 120, lat: ~ 2^10 } hitcount: 1
{ pid: 2585, prio: 120, lat: ~ 2^11 } hitcount: 379
{ pid: 2585, prio: 120, lat: ~ 2^12 } hitcount: 1008
{ pid: 2585, prio: 120, lat: ~ 2^13 } hitcount: 42
{ pid: 2585, prio: 120, lat: ~ 2^14 } hitcount: 18
{ pid: 2585, prio: 120, lat: ~ 2^15 } hitcount: 3
{ pid: 2585, prio: 120, lat: ~ 2^16 } hitcount: 1
{ pid: 2586, prio: 19, lat: ~ 2^11 } hitcount: 4715
{ pid: 2586, prio: 19, lat: ~ 2^12 } hitcount: 9161
{ pid: 2586, prio: 19, lat: ~ 2^13 } hitcount: 632
{ pid: 2586, prio: 19, lat: ~ 2^14 } hitcount: 47
{ pid: 2586, prio: 19, lat: ~ 2^15 } hitcount: 3
{ pid: 2586, prio: 19, lat: ~ 2^17 } hitcount: 1
{ pid: 2587, prio: 19, lat: ~ 2^11 } hitcount: 3398
{ pid: 2587, prio: 19, lat: ~ 2^12 } hitcount: 5762
{ pid: 2587, prio: 19, lat: ~ 2^13 } hitcount: 505
{ pid: 2587, prio: 19, lat: ~ 2^14 } hitcount: 58
{ pid: 2587, prio: 19, lat: ~ 2^15 } hitcount: 3
{ pid: 2587, prio: 19, lat: ~ 2^17 } hitcount: 1
Totals:
Hits: 25738
Entries: 19
Dropped: 0
Duplicates: 0
====
Example - wakeup latency with onmax()
====
This example is the same as the previous ones, but here we're
additionally using the onmax() action to save some context (several
fields of the sched_switch event) whenever the latency (wakeup_lat)
exceeds the previous maximum.
As with the similar functionality of the -RT latency_hist
histograms, it's useful to be able to capture information about the
previous process, which potentially could have contributed to the
maximum latency that was saved.
# echo 'wakeup_latency u64 lat; pid_t pid; int prio' >> \
/sys/kernel/debug/tracing/synthetic_events
# echo 'hist:keys=pid:ts0=$common_timestamp.usecs if comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
Here we add an onmax() action that saves some important fields of
the sched_switch event along with the maximum, in addition to
sending some of the same data to the synthetic event:
# echo 'hist:keys=next_pid:wakeup_lat=$common_timestamp.usecs-$ts0: \
onmax($wakeup_lat).save(next_comm,prev_pid,prev_prio,prev_comm): \
onmatch(sched.sched_wakeup).wakeup_latency($wakeup_lat,next_pid,next_prio) \
if next_comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_switch/trigger
# echo 'hist:keys=pid,prio,lat:sort=pid,lat' >> \
/sys/kernel/debug/tracing/events/synthetic/wakeup_latency/trigger
To see the maximums and associated data for each pid, cat the
sched_switch event, as that's the event the onmax() action is
associated with:
# cat /sys/kernel/debug/tracing/events/sched/sched_switch/hist
# event histogram
#
{ next_pid: 6173 } hitcount: 199
max: 14 next_comm: cyclictest prev_pid: 0 \
prev_prio: 120 prev_comm: swapper/2
{ next_pid: 6175 } hitcount: 1327
max: 15 next_comm: cyclictest prev_pid: 0 \
prev_prio: 120 prev_comm: swapper/2
{ next_pid: 6174 } hitcount: 1985
max: 30 next_comm: cyclictest prev_pid: 0 \
prev_prio: 120 prev_comm: swapper/3
Totals:
Hits: 17555
Entries: 3
Dropped: 0
Duplicates: 0
And, verifying, we can see that the max latencies captured above
match the highest latencies for each thread in the wakeup_latency
histogram:
# cat /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/hist
# event histogram
#
# trigger info: hist:keys=pid,prio,lat:vals=hitcount:sort=pid,lat:size=2048 [active]
#
{ pid: 6173, prio: 120, lat: 1 } hitcount: 2
{ pid: 6173, prio: 120, lat: 2 } hitcount: 82
{ pid: 6173, prio: 120, lat: 3 } hitcount: 84
...
{ pid: 6173, prio: 120, lat: 14 } hitcount: 1
{ pid: 6174, prio: 19, lat: 1 } hitcount: 23
{ pid: 6174, prio: 19, lat: 2 } hitcount: 749
...
{ pid: 6174, prio: 19, lat: 30 } hitcount: 1
{ pid: 6175, prio: 19, lat: 1 } hitcount: 20
{ pid: 6175, prio: 19, lat: 2 } hitcount: 633
...
{ pid: 6175, prio: 19, lat: 15 } hitcount: 1
Totals:
Hits: 3511
Entries: 38
Dropped: 0
Duplicates: 0
====
Example - combined wakeup and switchtime (wakeupswitch) latency
====
Finally, this example is quite a bit more involved, but that's
because it implements 3 latencies, one which is a combination of the
other two. This also, is something that the -RT latency_hist
patchset does and which this patchset adds generic support for.
The latency_hist patchset creates a few individual latency
histograms but also combines them into larger overall combined
histograms. For example, the time between when a thread is awakened
and when it actually continues executing in userspace is something
covered by a histogram, but it's also broken down into two
sub-histograms, one covering the time between sched_wakeup and the
time the thread is scheduled in (wakeup_latency as above), and the
time between when the thread is scheduled in and the time it
actually begins executing again (return from sys_nanosleep), covered
by a separate switchtime_latency histogram.
The below combines the wakeup_latency histogram from before, adds a
new switchtime_latency histogram, and another, wakeupswitch_latency,
that's a combination of the other two.
There isn't anything really new here, other than that the use of the
addition operator to add two latencies to produce the
wakeupswitch_latency.
First, we create the familiar wakeup_latency histogram:
# echo 'wakeup_latency u64 lat; pid_t pid' >> \
/sys/kernel/debug/tracing/synthetic_events
# 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:\
onmatch(sched.sched_wakeup).wakeup_latency($wakeup_lat,next_pid) \
if next_comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_switch/trigger
Here we save the wakeup_latency lat value as wakeup_lat for use
later in the combined latency:
# echo 'hist:keys=pid,lat:wakeup_lat=lat:sort=pid,lat' \
>> /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/trigger
Next, we create the switchtime_latency histogram:
# echo 'switchtime_latency u64 lat; pid_t pid' >> \
/sys/kernel/debug/tracing/synthetic_events
Here we save the sched_switch next_pid field as 'pid'. This is so
we can access the next_pid in the matching sys_exit_nanosleep event.
# echo 'hist:keys=next_pid:pid=next_pid:ts0=$common_timestamp.usecs \
if next_comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_switch/trigger
# echo 'hist:keys=common_pid:switchtime_lat=$common_timestamp.usecs-$ts0: \
onmatch(sched.sched_switch).switchtime_latency($switchtime_lat,$pid)' \
>> /sys/kernel/debug/tracing/events/syscalls/sys_exit_nanosleep/trigger
# echo 'hist:keys=pid,lat:sort=pid,lat' \
>> /sys/kernel/debug/tracing/events/synthetic/switchtime_latency/trigger
Finally, we create the combined wakeupswitch_latency:
# echo 'wakeupswitch_latency u64 lat; pid_t pid' >> \
/sys/kernel/debug/tracing/synthetic_events
Here we calculate the combined latency using the saved $wakeup_lat
variable from the wakeup_latency histogram and the lat value of the
switchtime_latency, save it as ws_lat and then use it to generate
the combined wakeupswitch latency:
# echo 'hist:keys=pid,lat:sort=pid,lat:ws_lat=$wakeup_lat+lat: \
onmatch(synthetic.wakeup_latency).wakeupswitch_latency($ws_lat,pid)' \
>> /sys/kernel/debug/tracing/events/synthetic/switchtime_latency/trigger
# echo 'hist:keys=pid,lat:sort=pid,lat' >> \
/sys/kernel/debug/tracing/events/synthetic/wakeupswitch_latency/trigger
After running our cyclictest workload, we can now look at each
histogram, starting with wakeup_latency:
# cat /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/hist
# event histogram
#
# trigger info: hist:keys=pid,lat:vals=hitcount:wakeup_lat=lat:sort=pid,lat:size=2048 [active]
#
{ pid: 6852, lat: 1 } hitcount: 2
{ pid: 6852, lat: 2 } hitcount: 88
{ pid: 6852, lat: 3 } hitcount: 72
{ pid: 6852, lat: 4 } hitcount: 19
{ pid: 6852, lat: 5 } hitcount: 10
{ pid: 6852, lat: 6 } hitcount: 1
{ pid: 6852, lat: 7 } hitcount: 4
{ pid: 6852, lat: 9 } hitcount: 2
{ pid: 6852, lat: 10 } hitcount: 1
{ pid: 6853, lat: 1 } hitcount: 1
{ pid: 6853, lat: 2 } hitcount: 535
{ pid: 6853, lat: 3 } hitcount: 1007
...
{ pid: 6853, lat: 19 } hitcount: 1
{ pid: 6853, lat: 20 } hitcount: 1
{ pid: 6853, lat: 29 } hitcount: 1
{ pid: 6854, lat: 1 } hitcount: 5
{ pid: 6854, lat: 2 } hitcount: 552
{ pid: 6854, lat: 3 } hitcount: 532
{ pid: 6854, lat: 4 } hitcount: 135
{ pid: 6854, lat: 5 } hitcount: 48
{ pid: 6854, lat: 6 } hitcount: 18
{ pid: 6854, lat: 7 } hitcount: 12
{ pid: 6854, lat: 8 } hitcount: 5
...
{ pid: 6854, lat: 29 } hitcount: 2
{ pid: 6854, lat: 48 } hitcount: 1
Totals:
Hits: 3501
Entries: 46
Dropped: 0
Duplicates: 0
Here's the switchtime histogram:
# cat /sys/kernel/debug/tracing/events/synthetic/switchtime_latency/hist
# event histogram
#
# trigger info: hist:keys=pid,lat:vals=hitcount:sort=pid,lat:size=2048 [active]
#
{ pid: 6852, lat: 1 } hitcount: 1
{ pid: 6852, lat: 2 } hitcount: 33
{ pid: 6852, lat: 3 } hitcount: 73
{ pid: 6852, lat: 4 } hitcount: 49
{ pid: 6852, lat: 5 } hitcount: 28
{ pid: 6852, lat: 6 } hitcount: 4
...
{ pid: 6852, lat: 16 } hitcount: 1
{ pid: 6853, lat: 1 } hitcount: 76
{ pid: 6853, lat: 2 } hitcount: 1190
{ pid: 6853, lat: 3 } hitcount: 475
{ pid: 6853, lat: 4 } hitcount: 125
{ pid: 6853, lat: 5 } hitcount: 40
{ pid: 6853, lat: 6 } hitcount: 21
{ pid: 6853, lat: 7 } hitcount: 13
{ pid: 6853, lat: 8 } hitcount: 5
{ pid: 6853, lat: 9 } hitcount: 11
...
{ pid: 6853, lat: 20 } hitcount: 2
{ pid: 6854, lat: 1 } hitcount: 13
{ pid: 6854, lat: 2 } hitcount: 799
{ pid: 6854, lat: 3 } hitcount: 332
{ pid: 6854, lat: 4 } hitcount: 97
{ pid: 6854, lat: 5 } hitcount: 29
{ pid: 6854, lat: 6 } hitcount: 16
...
{ pid: 6854, lat: 22 } hitcount: 1
{ pid: 6854, lat: 26 } hitcount: 1
Totals:
Hits: 3501
Entries: 48
Dropped: 0
Duplicates: 0
And here's the combined wakeupswitch latency histogram:
# cat /sys/kernel/debug/tracing/events/synthetic/wakeupswitch_latency/hist
# event histogram
#
# trigger info: hist:keys=pid,lat:vals=hitcount:sort=pid,lat:size=2048 [active]
#
{ pid: 6852, lat: 4 } hitcount: 24
{ pid: 6852, lat: 5 } hitcount: 47
{ pid: 6852, lat: 6 } hitcount: 53
{ pid: 6852, lat: 7 } hitcount: 29
...
{ pid: 6852, lat: 25 } hitcount: 1
{ pid: 6853, lat: 3 } hitcount: 5
{ pid: 6853, lat: 4 } hitcount: 504
{ pid: 6853, lat: 5 } hitcount: 753
{ pid: 6853, lat: 6 } hitcount: 337
{ pid: 6853, lat: 7 } hitcount: 149
{ pid: 6853, lat: 8 } hitcount: 66
{ pid: 6853, lat: 9 } hitcount: 45
{ pid: 6853, lat: 10 } hitcount: 24
{ pid: 6853, lat: 11 } hitcount: 26
{ pid: 6853, lat: 12 } hitcount: 9
{ pid: 6853, lat: 13 } hitcount: 13
{ pid: 6853, lat: 14 } hitcount: 9
{ pid: 6853, lat: 15 } hitcount: 3
{ pid: 6853, lat: 17 } hitcount: 3
{ pid: 6853, lat: 18 } hitcount: 5
{ pid: 6853, lat: 19 } hitcount: 2
...
{ pid: 6853, lat: 37 } hitcount: 1
{ pid: 6853, lat: 38 } hitcount: 1
{ pid: 6854, lat: 3 } hitcount: 7
{ pid: 6854, lat: 4 } hitcount: 418
{ pid: 6854, lat: 5 } hitcount: 493
{ pid: 6854, lat: 6 } hitcount: 165
{ pid: 6854, lat: 7 } hitcount: 81
{ pid: 6854, lat: 8 } hitcount: 57
{ pid: 6854, lat: 9 } hitcount: 27
{ pid: 6854, lat: 10 } hitcount: 15
{ pid: 6854, lat: 11 } hitcount: 13
{ pid: 6854, lat: 12 } hitcount: 7
{ pid: 6854, lat: 13 } hitcount: 10
{ pid: 6854, lat: 14 } hitcount: 6
{ pid: 6854, lat: 15 } hitcount: 5
{ pid: 6854, lat: 16 } hitcount: 5
{ pid: 6854, lat: 17 } hitcount: 2
{ pid: 6854, lat: 18 } hitcount: 1
...
{ pid: 6854, lat: 55 } hitcount: 1
Totals:
Hits: 3501
Entries: 70
Dropped: 0
Duplicates: 0
Finally, just to show that synthetic events are indeed just like any
other event as far as the event subsystem is concerned, we can
enable the synthetic events and see the events appear in the trace
buffer:
# echo 1 > /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/enable
# echo 1 > /sys/kernel/debug/tracing/events/synthetic/switchtime_latency/enable
# echo 1 > /sys/kernel/debug/tracing/events/synthetic/wakeupswitch_latency/enable
Below is a snippet of the contents of the trace file produced when
the above histograms were generated:
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 10503/10503 #P:4
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
<idle>-0 [001] d..2 23532.240146: wakeup_latency: lat=4, pid=6853
cyclictest-6853 [001] .... 23532.240153: switchtime_latency: lat=7, pid=6853
cyclictest-6853 [001] .... 23532.240157: wakeupswitch_latency: lat=11, pid=6853
gnome-terminal--2500 [001] d..2 23532.240672: wakeup_latency: lat=5, pid=6854
cyclictest-6854 [001] .... 23532.240676: switchtime_latency: lat=4, pid=6854
cyclictest-6854 [001] .... 23532.240677: wakeupswitch_latency: lat=9, pid=6854
gnome-terminal--2500 [001] d..2 23532.241169: wakeup_latency: lat=4, pid=6853
cyclictest-6853 [001] .... 23532.241172: switchtime_latency: lat=3, pid=6853
cyclictest-6853 [001] .... 23532.241174: wakeupswitch_latency: lat=7, pid=6853
<idle>-0 [001] d..2 23532.242189: wakeup_latency: lat=6, pid=6853
cyclictest-6853 [001] .... 23532.242195: switchtime_latency: lat=8, pid=6853
<idle>-0 [000] d..2 23532.242196: wakeup_latency: lat=12, pid=6854
cyclictest-6853 [001] .... 23532.240146: wakeupswitch_latency: lat=14, pid=6853
cyclictest-6854 [000] .... 23532.242196: switchtime_latency: lat=4, pid=6854
<idle>-0 [001] d..2 23532.240146: wakeup_latency: lat=2, pid=6853
cyclictest-6854 [000] .... 23532.242196: wakeupswitch_latency: lat=16, pid=6854
cyclictest-6853 [001] .... 23532.240146: switchtime_latency: lat=3, pid=6853
...
One quick note about usage - the introduction of variables and
actions obviously makes it harder to determine the cause of a hist
trigger command failure - 'Invalid argument' is no longer sufficient
in many cases.
For that reason, a new 'extended error' mechanism has been added to
hist triggers, initially focused on variable and action-related
errors, but something that could possibly expanded to other error
conditions later.
To make use of it, simply read the 'hist' file of the event that was
the target of the command.
In this example, we've entered the same command twice, resulting in
an attempt to define the same variable (ts0) twice. After seeing
the 'Invalid argument' error for the command, we read the same
event's hist file and see a message to that effect at the bottom of
the file:
# echo 'hist:keys=pid:ts0=$common_timestamp.usecs if comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
# echo 'hist:keys=pid:ts0=$common_timestamp.usecs if comm=="cyclictest"' >> \
/sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
-su: echo: write error: Invalid argument
# cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/hist
# event histogram
#
#
Totals:
Hits: 0
Entries: 0
Dropped: 0
Duplicates: 0
ERROR: Variable already defined: ts0
Last command: keys=pid:ts0=$common_timestamp.usecs if comm=="cyclictest"
The following changes since commit 681bec0367c2606b6154060310a2ffa543175980:
tracing: Rename update the enum_map file (2017-06-13 17:13:06 -0400)
are available in the git repository at:
https://github.com/tzanussi/linux-trace-inter-event.git tzanussi/inter-event-v01
https://github.com/tzanussi/linux-trace-inter-event/tree/tzanussi/inter-event-v01
Tom Zanussi (32):
tracing: Add hist_field_name() accessor
tracing: Reimplement log2
ring-buffer: Add interface for setting absolute time stamps
ring-buffer: Redefine the unimplemented RINGBUF_TIME_TIME_STAMP
tracing: Give event triggers access to ring_buffer_event
tracing: Add ring buffer event param to hist field functions
tracing: Increase tracing map KEYS_MAX size
tracing: Break out hist trigger assignment parsing
tracing: Make traceprobe parsing code reusable
tracing: Add NO_DISCARD event file flag
tracing: Add post-trigger flag to hist trigger command
tracing: Add hist trigger timestamp support
tracing: Add per-element variable support to tracing_map
tracing: Add hist_data member to hist_field
tracing: Add usecs modifier for hist trigger timestamps
tracing: Add variable support to hist triggers
tracing: Account for variables in named trigger compatibility
tracing: Add simple expression support to hist triggers
tracing: Add variable reference handling to hist triggers
tracing: Add support for dynamic tracepoints
tracing: Add hist trigger action hook
tracing: Add support for 'synthetic' events
tracing: Add 'onmatch' hist trigger action support
tracing: Add 'onmax' hist trigger action support
tracing: Allow whitespace to surround hist trigger filter
tracing: Make duplicate count from tracing_map available
tracing: Add cpu field for hist triggers
tracing: Add hist trigger support for variable reference aliases
tracing: Add 'last error' error facility for hist triggers
tracing: Add inter-event hist trigger Documentation
tracing: Make tracing_set_clock() non-static
tracing: Add a clock attribute for hist triggers
Documentation/trace/events.txt | 422 ++++
include/linux/ring_buffer.h | 14 +-
include/linux/trace_events.h | 17 +-
include/linux/tracepoint.h | 11 +-
kernel/trace/ring_buffer.c | 118 +-
kernel/trace/trace.c | 121 +-
kernel/trace/trace.h | 36 +-
kernel/trace/trace_events.c | 4 +-
kernel/trace/trace_events_hist.c | 4107 ++++++++++++++++++++++++++++++++---
kernel/trace/trace_events_trigger.c | 69 +-
kernel/trace/trace_kprobe.c | 18 +-
kernel/trace/trace_probe.c | 86 -
kernel/trace/trace_probe.h | 7 -
kernel/trace/trace_uprobe.c | 2 +-
kernel/trace/tracing_map.c | 125 +-
kernel/trace/tracing_map.h | 16 +-
kernel/tracepoint.c | 42 +-
17 files changed, 4684 insertions(+), 531 deletions(-)
--
1.9.3
[toc] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 17/32] tracing: Account for variables in named trigger compatibility |
| Message-ID | <tWL97-8u4-1@gated-at.bofh.it> |
| In reply to | #1675146 |
Named triggers must also have the same set of variables in order to be
considered compatible - update the trigger match test to account for
that.
The reason for this requirement is that named triggers with variables
are meant to allow one or more events to set the same variable.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace_events_hist.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 75cac76..fa8f607 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -1545,7 +1545,7 @@ static int event_hist_trigger_print(struct seq_file *m,
sort_key = &hist_data->sort_keys[i];
idx = sort_key->field_idx;
- if (WARN_ON(idx >= TRACING_MAP_FIELDS_MAX))
+ if (WARN_ON(idx >= HIST_FIELDS_MAX))
return -EINVAL;
if (i > 0)
@@ -1733,6 +1733,12 @@ static bool hist_trigger_match(struct event_trigger_data *data,
return false;
if (key_field->is_signed != key_field_test->is_signed)
return false;
+ if ((key_field->var.name && !key_field_test->var.name) ||
+ (!key_field->var.name && key_field_test->var.name))
+ return false;
+ if ((key_field->var.name && key_field_test->var.name) &&
+ strcmp(key_field->var.name, key_field_test->var.name) != 0)
+ return false;
}
for (i = 0; i < hist_data->n_sort_keys; i++) {
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 02/32] tracing: Reimplement log2 |
| Message-ID | <tWL97-8u4-3@gated-at.bofh.it> |
| In reply to | #1675146 |
log2 as currently implemented applies only to u64 trace_event_field
derived fields, and assumes that anything it's applied to is a u64
field.
To prepare for synthetic fields like latencies, log2 should be
applicable to those as well, so take the opportunity now to fix the
current problems as well as expand to more general uses.
log2 should be thought of as a chaining function rather than a field
type. To enable this as well as possible future function
implementations, add a hist_field operand array into the hist_field
definition for this purpose, and make use of it to implement the log2
'function'.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace_events_hist.c | 31 +++++++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 91ffc39..7b55956 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -28,12 +28,16 @@
typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event);
+#define HIST_FIELD_OPERANDS_MAX 2
+
struct hist_field {
struct ftrace_event_field *field;
unsigned long flags;
hist_field_fn_t fn;
unsigned int size;
unsigned int offset;
+ unsigned int is_signed;
+ struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
};
static u64 hist_field_none(struct hist_field *field, void *event)
@@ -71,7 +75,9 @@ static u64 hist_field_pstring(struct hist_field *hist_field, void *event)
static u64 hist_field_log2(struct hist_field *hist_field, void *event)
{
- u64 val = *(u64 *)(event + hist_field->field->offset);
+ struct hist_field *operand = hist_field->operands[0];
+
+ u64 val = operand->fn(operand, event);
return (u64) ilog2(roundup_pow_of_two(val));
}
@@ -156,6 +162,8 @@ static const char *hist_field_name(struct hist_field *field,
if (field->field)
field_name = field->field->name;
+ else if (field->flags & HIST_FIELD_FL_LOG2)
+ field_name = hist_field_name(field->operands[0], ++level);
if (field_name == NULL)
field_name = "";
@@ -357,8 +365,20 @@ static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt)
.elt_init = hist_trigger_elt_comm_init,
};
-static void destroy_hist_field(struct hist_field *hist_field)
+static void destroy_hist_field(struct hist_field *hist_field,
+ unsigned int level)
{
+ unsigned int i;
+
+ if (level > 2)
+ return;
+
+ if (!hist_field)
+ return;
+
+ for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++)
+ destroy_hist_field(hist_field->operands[i], ++level);
+
kfree(hist_field);
}
@@ -385,7 +405,10 @@ static struct hist_field *create_hist_field(struct ftrace_event_field *field,
}
if (flags & HIST_FIELD_FL_LOG2) {
+ unsigned long fl = flags & ~HIST_FIELD_FL_LOG2;
hist_field->fn = hist_field_log2;
+ hist_field->operands[0] = create_hist_field(field, fl);
+ hist_field->size = hist_field->operands[0]->size;
goto out;
}
@@ -405,7 +428,7 @@ static struct hist_field *create_hist_field(struct ftrace_event_field *field,
hist_field->fn = select_value_fn(field->size,
field->is_signed);
if (!hist_field->fn) {
- destroy_hist_field(hist_field);
+ destroy_hist_field(hist_field, 0);
return NULL;
}
}
@@ -422,7 +445,7 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) {
if (hist_data->fields[i]) {
- destroy_hist_field(hist_data->fields[i]);
+ destroy_hist_field(hist_data->fields[i], 0);
hist_data->fields[i] = NULL;
}
}
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 10/32] tracing: Add NO_DISCARD event file flag |
| Message-ID | <tWL97-8u4-7@gated-at.bofh.it> |
| In reply to | #1675146 |
Whenever an event_command has a post-trigger that needs access to the
event record, the event record can't be discarded, or the post-trigger
will eventually see bogus data.
In order to allow the discard check to treat this case separately, add
an EVENT_FILE_FL_NO_DISCARD flag to the event file flags, along with
code in the discard check that checks the flag and avoids the discard
when the flag is set.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
include/linux/trace_events.h | 3 +++
kernel/trace/trace.h | 13 ++++++++++---
kernel/trace/trace_events_trigger.c | 16 +++++++++++++---
3 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index f333114..d7b230b 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -297,6 +297,7 @@ enum {
EVENT_FILE_FL_TRIGGER_MODE_BIT,
EVENT_FILE_FL_TRIGGER_COND_BIT,
EVENT_FILE_FL_PID_FILTER_BIT,
+ EVENT_FILE_FL_NO_DISCARD_BIT,
};
/*
@@ -311,6 +312,7 @@ enum {
* TRIGGER_MODE - When set, invoke the triggers associated with the event
* TRIGGER_COND - When set, one or more triggers has an associated filter
* PID_FILTER - When set, the event is filtered based on pid
+ * NO_DISCARD - When set, do not discard events, something needs them later
*/
enum {
EVENT_FILE_FL_ENABLED = (1 << EVENT_FILE_FL_ENABLED_BIT),
@@ -322,6 +324,7 @@ enum {
EVENT_FILE_FL_TRIGGER_MODE = (1 << EVENT_FILE_FL_TRIGGER_MODE_BIT),
EVENT_FILE_FL_TRIGGER_COND = (1 << EVENT_FILE_FL_TRIGGER_COND_BIT),
EVENT_FILE_FL_PID_FILTER = (1 << EVENT_FILE_FL_PID_FILTER_BIT),
+ EVENT_FILE_FL_NO_DISCARD = (1 << EVENT_FILE_FL_NO_DISCARD_BIT),
};
struct trace_event_file {
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 78c7694..81b1d07 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1271,9 +1271,16 @@ static inline void trace_buffer_unlock_commit(struct trace_array *tr,
if (eflags & EVENT_FILE_FL_TRIGGER_COND)
*tt = event_triggers_call(file, entry, event);
- if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags) ||
- (unlikely(file->flags & EVENT_FILE_FL_FILTERED) &&
- !filter_match_preds(file->filter, entry))) {
+ if (unlikely(file->flags & EVENT_FILE_FL_FILTERED) &&
+ !filter_match_preds(file->filter, entry)) {
+ __trace_event_discard_commit(buffer, event);
+ return true;
+ }
+
+ if (test_bit(EVENT_FILE_FL_NO_DISCARD_BIT, &file->flags))
+ return false;
+
+ if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags)) {
__trace_event_discard_commit(buffer, event);
return true;
}
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 9b0fe31..d345820 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -505,20 +505,30 @@ int trace_event_trigger_enable_disable(struct trace_event_file *file,
void update_cond_flag(struct trace_event_file *file)
{
struct event_trigger_data *data;
- bool set_cond = false;
+ bool set_cond = false, set_no_discard = false;
list_for_each_entry_rcu(data, &file->triggers, list) {
if (data->filter || event_command_post_trigger(data->cmd_ops) ||
- event_command_needs_rec(data->cmd_ops)) {
+ event_command_needs_rec(data->cmd_ops))
set_cond = true;
+
+ if (event_command_post_trigger(data->cmd_ops) &&
+ event_command_needs_rec(data->cmd_ops))
+ set_no_discard = true;
+
+ if (set_cond && set_no_discard)
break;
- }
}
if (set_cond)
set_bit(EVENT_FILE_FL_TRIGGER_COND_BIT, &file->flags);
else
clear_bit(EVENT_FILE_FL_TRIGGER_COND_BIT, &file->flags);
+
+ if (set_no_discard)
+ set_bit(EVENT_FILE_FL_NO_DISCARD_BIT, &file->flags);
+ else
+ clear_bit(EVENT_FILE_FL_NO_DISCARD_BIT, &file->flags);
}
/**
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 11/32] tracing: Add post-trigger flag to hist trigger command |
| Message-ID | <tWL98-8u4-23@gated-at.bofh.it> |
| In reply to | #1675146 |
Add EVENT_CMD_FL_POST_TRIGGER to the hist trigger cmd - it doesn't
affect the hist trigger results, and allows further events such as
synthetic events to be generated from a hist trigger.
Without this change, generating an event from a hist trigger will
cause the generated event to fail a ring buffer trace_recursive_lock()
check and return without actually logging the event.
---
kernel/trace/trace_events_hist.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index fccbffe..dab6ff6 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -1676,7 +1676,7 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
static struct event_command trigger_hist_cmd = {
.name = "hist",
.trigger_type = ETT_EVENT_HIST,
- .flags = EVENT_CMD_FL_NEEDS_REC,
+ .flags = EVENT_CMD_FL_NEEDS_REC | EVENT_CMD_FL_POST_TRIGGER,
.func = event_hist_trigger_func,
.reg = hist_register_trigger,
.unreg = hist_unregister_trigger,
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 19/32] tracing: Add variable reference handling to hist triggers |
| Message-ID | <tWL97-8u4-5@gated-at.bofh.it> |
| In reply to | #1675146 |
Add the necessary infrastructure to allow the variables defined on one
event to be referenced in another. This allows variables set by a
previous event to be referenced and used in expressions combining the
variable values saved by that previous event and the event fields of
the current event. For example, here's how a latency can be
calculated and saved into yet another variable named 'wakeup_lat':
# echo 'hist:keys=pid,prio:ts0=common_timestamp ...
# echo 'hist:keys=next_pid:wakeup_lat=common_timestamp-$ts0 ...
In the first event, the event's timetamp is saved into the variable
ts0. In the next line, ts0 is subtracted from the second event's
timestamp to produce the latency.
Further users of variable references will be described in subsequent
patches, such as for instance how the 'wakeup_lat' variable above can
be displayed in a latency histogram.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.h | 2 +
kernel/trace/trace_events_hist.c | 719 ++++++++++++++++++++++++++++++------
kernel/trace/trace_events_trigger.c | 6 +
3 files changed, 604 insertions(+), 123 deletions(-)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 65f90b4..7dc13fa 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1528,6 +1528,8 @@ extern int save_named_trigger(const char *name,
extern void unpause_named_trigger(struct event_trigger_data *data);
extern void set_named_trigger_data(struct event_trigger_data *data,
struct event_trigger_data *named_data);
+extern struct event_trigger_data *
+get_named_trigger_data(struct event_trigger_data *data);
extern int register_event_command(struct event_command *cmd);
extern int unregister_event_command(struct event_command *cmd);
extern int register_trigger_hist_enable_disable_cmds(void);
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 92a9d89..d83d4ca 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -26,8 +26,10 @@
struct hist_field;
-typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event,
- struct ring_buffer_event *rbe);
+typedef u64 (*hist_field_fn_t) (struct hist_field *field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event);
#define HIST_FIELD_OPERANDS_MAX 2
#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
@@ -57,30 +59,41 @@ struct hist_field {
struct hist_var var;
enum field_op_id operator;
char *name;
+ unsigned int var_idx;
+ unsigned int var_ref_idx;
+ bool read_once;
};
-static u64 hist_field_none(struct hist_field *field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_none(struct hist_field *field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
return 0;
}
-static u64 hist_field_counter(struct hist_field *field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_counter(struct hist_field *field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
return 1;
}
-static u64 hist_field_string(struct hist_field *hist_field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_string(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
char *addr = (char *)(event + hist_field->field->offset);
return (u64)(unsigned long)addr;
}
-static u64 hist_field_dynstring(struct hist_field *hist_field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_dynstring(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
u32 str_item = *(u32 *)(event + hist_field->field->offset);
int str_loc = str_item & 0xffff;
@@ -89,54 +102,64 @@ static u64 hist_field_dynstring(struct hist_field *hist_field, void *event,
return (u64)(unsigned long)addr;
}
-static u64 hist_field_pstring(struct hist_field *hist_field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_pstring(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
char **addr = (char **)(event + hist_field->field->offset);
return (u64)(unsigned long)*addr;
}
-static u64 hist_field_log2(struct hist_field *hist_field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_log2(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
struct hist_field *operand = hist_field->operands[0];
- u64 val = operand->fn(operand, event, rbe);
+ u64 val = operand->fn(operand, elt, rbe, event);
return (u64) ilog2(roundup_pow_of_two(val));
}
-static u64 hist_field_plus(struct hist_field *hist_field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_plus(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
struct hist_field *operand1 = hist_field->operands[0];
struct hist_field *operand2 = hist_field->operands[1];
- u64 val1 = operand1->fn(operand1, event, rbe);
- u64 val2 = operand2->fn(operand2, event, rbe);
+ u64 val1 = operand1->fn(operand1, elt, rbe, event);
+ u64 val2 = operand2->fn(operand2, elt, rbe, event);
return val1 + val2;
}
-static u64 hist_field_minus(struct hist_field *hist_field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_minus(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
struct hist_field *operand1 = hist_field->operands[0];
struct hist_field *operand2 = hist_field->operands[1];
- u64 val1 = operand1->fn(operand1, event, rbe);
- u64 val2 = operand2->fn(operand2, event, rbe);
+ u64 val1 = operand1->fn(operand1, elt, rbe, event);
+ u64 val2 = operand2->fn(operand2, elt, rbe, event);
return val1 - val2;
}
-static u64 hist_field_unary_minus(struct hist_field *hist_field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_unary_minus(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
struct hist_field *operand = hist_field->operands[0];
- s64 sval = (s64)operand->fn(operand, event, rbe);
+ s64 sval = (s64)operand->fn(operand, elt, rbe, event);
u64 val = (u64)-sval;
return val;
@@ -144,8 +167,9 @@ static u64 hist_field_unary_minus(struct hist_field *hist_field, void *event,
#define DEFINE_HIST_FIELD_FN(type) \
static u64 hist_field_##type(struct hist_field *hist_field, \
- void *event, \
- struct ring_buffer_event *rbe) \
+ struct tracing_map_elt *elt, \
+ struct ring_buffer_event *rbe, \
+ void *event) \
{ \
type *addr = (type *)(event + hist_field->field->offset); \
\
@@ -193,6 +217,7 @@ enum hist_field_flags {
HIST_FIELD_FL_VAR = 4096,
HIST_FIELD_FL_VAR_ONLY = 8192,
HIST_FIELD_FL_EXPR = 16384,
+ HIST_FIELD_FL_VAR_REF = 32768,
};
struct hist_trigger_attrs {
@@ -225,10 +250,14 @@ struct hist_trigger_data {
struct tracing_map *map;
bool enable_timestamps;
bool remove;
+ struct hist_field *var_refs[TRACING_MAP_VARS_MAX];
+ unsigned int n_var_refs;
};
-static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
- struct ring_buffer_event *rbe)
+static u64 hist_field_timestamp(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
{
struct hist_trigger_data *hist_data = hist_field->hist_data;
struct trace_array *tr = hist_data->event_file->tr;
@@ -241,6 +270,324 @@ static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
return ts;
}
+static LIST_HEAD(hist_var_list);
+
+struct hist_var_data {
+ struct list_head list;
+ struct hist_trigger_data *hist_data;
+};
+
+static struct hist_field *check_var_ref(struct hist_field *hist_field,
+ struct hist_trigger_data *var_data,
+ unsigned int var_idx)
+{
+ struct hist_field *found = NULL;
+
+ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF) {
+ if (hist_field->var.idx == var_idx &&
+ hist_field->var.hist_data == var_data) {
+ found = hist_field;
+ }
+ }
+
+ return found;
+}
+
+static struct hist_field *find_var_ref(struct hist_trigger_data *hist_data,
+ struct hist_trigger_data *var_data,
+ unsigned int var_idx)
+{
+ struct hist_field *hist_field, *found = NULL;
+ unsigned int i, j;
+
+ for_each_hist_field(i, hist_data) {
+ hist_field = hist_data->fields[i];
+ found = check_var_ref(hist_field, var_data, var_idx);
+ if (found)
+ return found;
+
+ for (j = 0; j < HIST_FIELD_OPERANDS_MAX; j++) {
+ struct hist_field *operand;
+
+ operand = hist_field->operands[j];
+ found = check_var_ref(operand, var_data, var_idx);
+ if (found)
+ return found;
+ }
+ }
+
+ return found;
+}
+
+static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data,
+ unsigned int var_idx)
+{
+ struct hist_field *found = NULL;
+ struct hist_var_data *var_data;
+
+ list_for_each_entry(var_data, &hist_var_list, list) {
+ found = find_var_ref(var_data->hist_data, hist_data, var_idx);
+ if (found)
+ break;
+ }
+
+ return found;
+}
+
+static bool check_var_refs(struct hist_trigger_data *hist_data)
+{
+ struct hist_field *field;
+ bool found = false;
+ int i;
+
+ for_each_hist_field(i, hist_data) {
+ field = hist_data->fields[i];
+ if (field && field->flags & HIST_FIELD_FL_VAR) {
+ if (find_any_var_ref(hist_data, field->var.idx)) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ return found;
+}
+
+static struct hist_var_data *find_hist_vars(struct hist_trigger_data *hist_data)
+{
+ struct hist_var_data *var_data, *found = NULL;
+
+ list_for_each_entry(var_data, &hist_var_list, list) {
+ if (var_data->hist_data == hist_data) {
+ found = var_data;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static bool has_hist_vars(struct hist_trigger_data *hist_data)
+{
+ struct hist_field *hist_field;
+ bool found = false;
+ int i;
+
+ for_each_hist_field(i, hist_data) {
+ hist_field = hist_data->fields[i];
+ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR) {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static int save_hist_vars(struct hist_trigger_data *hist_data)
+{
+ struct hist_var_data *var_data;
+
+ var_data = find_hist_vars(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_var_list);
+
+ return 0;
+}
+
+static void remove_hist_vars(struct hist_trigger_data *hist_data)
+{
+ struct hist_var_data *var_data;
+
+ var_data = find_hist_vars(hist_data);
+ if (!var_data)
+ return;
+
+ if (WARN_ON(check_var_refs(hist_data)))
+ return;
+
+ list_del(&var_data->list);
+
+ kfree(var_data);
+}
+
+static struct hist_field *find_var_field(struct hist_trigger_data *hist_data,
+ const char *var_name)
+{
+ struct hist_field *hist_field, *found = NULL;
+ int i;
+
+ for_each_hist_field(i, hist_data) {
+ hist_field = hist_data->fields[i];
+ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR &&
+ strcmp(hist_field->var.name, var_name) == 0) {
+ found = hist_field;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static struct hist_field *find_var(struct trace_event_file *file,
+ const char *var_name)
+{
+ struct hist_trigger_data *hist_data;
+ struct event_trigger_data *test;
+ struct hist_field *hist_field;
+
+ list_for_each_entry_rcu(test, &file->triggers, list) {
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+ hist_data = test->private_data;
+ hist_field = find_var_field(hist_data, var_name);
+ if (hist_field)
+ return hist_field;
+ }
+ }
+
+ return NULL;
+}
+
+static struct trace_event_file *find_var_file(const char *system,
+ const char *event_name,
+ const char *var_name)
+{
+ struct hist_trigger_data *var_hist_data;
+ struct hist_var_data *var_data;
+ struct trace_event_call *call;
+ struct trace_event_file *file;
+ const char *name;
+
+ list_for_each_entry(var_data, &hist_var_list, list) {
+ var_hist_data = var_data->hist_data;
+ file = var_hist_data->event_file;
+ call = file->event_call;
+ name = trace_event_name(call);
+
+ if (!system || !event_name) {
+ if (find_var(file, var_name))
+ return file;
+ continue;
+ }
+
+ if (strcmp(event_name, name) != 0)
+ continue;
+ if (strcmp(system, call->class->system) != 0)
+ continue;
+
+ return file;
+ }
+
+ return NULL;
+}
+
+static struct hist_field *find_file_var(struct trace_event_file *file,
+ const char *var_name)
+{
+ struct hist_trigger_data *test_data;
+ struct event_trigger_data *test;
+ struct hist_field *hist_field;
+
+ list_for_each_entry_rcu(test, &file->triggers, list) {
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+ test_data = test->private_data;
+ hist_field = find_var_field(test_data, var_name);
+ if (hist_field)
+ return hist_field;
+ }
+ }
+
+ return NULL;
+}
+
+static struct hist_field *find_event_var(const char *system,
+ const char *event_name,
+ const char *var_name)
+{
+ struct hist_field *hist_field = NULL;
+ struct trace_event_file *file;
+
+ file = find_var_file(system, event_name, var_name);
+ if (!file)
+ return NULL;
+
+ hist_field = find_file_var(file, var_name);
+
+ return hist_field;
+}
+
+struct hist_elt_data {
+ char *comm;
+ u64 *var_ref_vals;
+};
+
+static u64 hist_field_var_ref(struct hist_field *hist_field,
+ struct tracing_map_elt *elt,
+ struct ring_buffer_event *rbe,
+ void *event)
+{
+ struct hist_elt_data *elt_data;
+ u64 var_val = 0;
+
+ elt_data = elt->private_data;
+ var_val = elt_data->var_ref_vals[hist_field->var_ref_idx];
+
+ return var_val;
+}
+
+static bool resolve_var_refs(struct hist_trigger_data *hist_data, void *key,
+ u64 *var_ref_vals, bool self)
+{
+ struct hist_trigger_data *var_data;
+ struct tracing_map_elt *var_elt;
+ struct hist_field *hist_field;
+ unsigned int i, var_idx;
+ bool resolved = true;
+ u64 var_val = 0;
+
+ for (i = 0; i < hist_data->n_var_refs; i++) {
+ hist_field = hist_data->var_refs[i];
+ var_idx = hist_field->var.idx;
+ var_data = hist_field->var.hist_data;
+
+ if (var_data == NULL) {
+ resolved = false;
+ break;
+ }
+
+ if ((self && var_data != hist_data) ||
+ (!self && var_data == hist_data))
+ continue;
+
+ var_elt = tracing_map_lookup(var_data->map, key);
+ if (!var_elt) {
+ resolved = false;
+ break;
+ }
+
+ if (!tracing_map_var_set(var_elt, var_idx)) {
+ resolved = false;
+ break;
+ }
+
+ if (self || !hist_field->read_once)
+ var_val = tracing_map_read_var(var_elt, var_idx);
+ else
+ var_val = tracing_map_read_var_once(var_elt, var_idx);
+
+ var_ref_vals[i] = var_val;
+ }
+
+ return resolved;
+}
+
static const char *hist_field_name(struct hist_field *field,
unsigned int level)
{
@@ -255,7 +602,8 @@ static const char *hist_field_name(struct hist_field *field,
field_name = hist_field_name(field->operands[0], ++level);
else if (field->flags & HIST_FIELD_FL_TIMESTAMP)
field_name = "$common_timestamp";
- else if (field->flags & HIST_FIELD_FL_EXPR)
+ else if (field->flags & HIST_FIELD_FL_EXPR ||
+ field->flags & HIST_FIELD_FL_VAR_REF)
field_name = field->name;
if (field_name == NULL)
@@ -439,26 +787,36 @@ static inline void save_comm(char *comm, struct task_struct *task)
memcpy(comm, task->comm, TASK_COMM_LEN);
}
-static void hist_trigger_elt_comm_free(struct tracing_map_elt *elt)
+static void hist_trigger_elt_data_free(struct tracing_map_elt *elt)
{
- kfree((char *)elt->private_data);
+ struct hist_elt_data *private_data = elt->private_data;
+
+ kfree(private_data->comm);
+ kfree(private_data);
}
-static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt)
+static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt)
{
struct hist_trigger_data *hist_data = elt->map->private_data;
+ unsigned int size = TASK_COMM_LEN + 1;
+ struct hist_elt_data *elt_data;
struct hist_field *key_field;
unsigned int i;
+ elt->private_data = elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL);
+ if (!elt_data)
+ return -ENOMEM;
+
for_each_hist_key_field(i, hist_data) {
key_field = hist_data->fields[i];
if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
- unsigned int size = TASK_COMM_LEN + 1;
-
- elt->private_data = kzalloc(size, GFP_KERNEL);
- if (!elt->private_data)
+ elt_data->comm = kzalloc(size, GFP_KERNEL);
+ if (!elt_data->comm) {
+ kfree(elt_data);
+ elt->private_data = NULL;
return -ENOMEM;
+ }
break;
}
}
@@ -466,29 +824,31 @@ static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt)
return 0;
}
-static void hist_trigger_elt_comm_copy(struct tracing_map_elt *to,
+static void hist_trigger_elt_data_copy(struct tracing_map_elt *to,
struct tracing_map_elt *from)
{
- char *comm_from = from->private_data;
- char *comm_to = to->private_data;
+ struct hist_elt_data *from_data = from->private_data;
+ struct hist_elt_data *to_data = to->private_data;
- if (comm_from)
- memcpy(comm_to, comm_from, TASK_COMM_LEN + 1);
+ memcpy(to_data, from_data, sizeof(*to));
+
+ if (from_data->comm)
+ memcpy(to_data->comm, from_data->comm, TASK_COMM_LEN + 1);
}
-static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt)
+static void hist_trigger_elt_data_init(struct tracing_map_elt *elt)
{
- char *comm = elt->private_data;
+ struct hist_elt_data *private_data = elt->private_data;
- if (comm)
- save_comm(comm, current);
+ if (private_data->comm)
+ save_comm(private_data->comm, current);
}
-static const struct tracing_map_ops hist_trigger_elt_comm_ops = {
- .elt_alloc = hist_trigger_elt_comm_alloc,
- .elt_copy = hist_trigger_elt_comm_copy,
- .elt_free = hist_trigger_elt_comm_free,
- .elt_init = hist_trigger_elt_comm_init,
+static const struct tracing_map_ops hist_trigger_elt_data_ops = {
+ .elt_alloc = hist_trigger_elt_data_alloc,
+ .elt_copy = hist_trigger_elt_data_copy,
+ .elt_free = hist_trigger_elt_data_free,
+ .elt_init = hist_trigger_elt_data_init,
};
static char *expr_str(struct hist_field *field, unsigned int level)
@@ -513,6 +873,8 @@ static char *expr_str(struct hist_field *field, unsigned int level)
return expr;
}
+ if (field->operands[0]->flags & HIST_FIELD_FL_VAR_REF)
+ strcat(expr, "$");
strcat(expr, hist_field_name(field->operands[0], 0));
switch (field->operator) {
@@ -527,6 +889,8 @@ static char *expr_str(struct hist_field *field, unsigned int level)
return NULL;
}
+ if (field->operands[1]->flags & HIST_FIELD_FL_VAR_REF)
+ strcat(expr, "$");
strcat(expr, hist_field_name(field->operands[1], 0));
return expr;
@@ -597,6 +961,11 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
if (flags & HIST_FIELD_FL_EXPR)
goto out; /* caller will populate */
+ if (flags & HIST_FIELD_FL_VAR_REF) {
+ hist_field->fn = hist_field_var_ref;
+ goto out;
+ }
+
if (flags & HIST_FIELD_FL_HITCOUNT) {
hist_field->fn = hist_field_counter;
goto out;
@@ -669,6 +1038,44 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
}
}
+static struct hist_field *create_var_ref(struct hist_field *var_field)
+{
+ unsigned long flags = HIST_FIELD_FL_VAR_REF;
+ struct hist_field *ref_field;
+
+ ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL);
+ if (ref_field) {
+ ref_field->var.idx = var_field->var.idx;
+ ref_field->var.hist_data = var_field->hist_data;
+ ref_field->size = var_field->size;
+ ref_field->is_signed = var_field->is_signed;
+ ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL);
+ if (!ref_field->name) {
+ destroy_hist_field(ref_field, 0);
+ return NULL;
+ }
+ }
+
+ return ref_field;
+}
+
+static struct hist_field *parse_var_ref(char *system, char *event_name,
+ char *var_name)
+{
+ struct hist_field *var_field = NULL, *ref_field = NULL;
+
+ if (!var_name || strlen(var_name) < 2 || var_name[0] != '$')
+ return NULL;
+
+ var_name++;
+
+ var_field = find_event_var(system, event_name, var_name);
+ if (var_field)
+ ref_field = create_var_ref(var_field);
+
+ return ref_field;
+}
+
static struct ftrace_event_field *
parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
char *field_str, unsigned long *flags)
@@ -715,10 +1122,28 @@ struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
struct trace_event_file *file, char *str,
unsigned long *flags, char *var_name)
{
+ char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str;
struct ftrace_event_field *field = NULL;
struct hist_field *hist_field = NULL;
int ret = 0;
+ s = strchr(str, '.');
+ if (s) {
+ s = strchr(++s, '.');
+ if (s) {
+ ref_system = strsep(&str, ".");
+ ref_event = strsep(&str, ".");
+ ref_var = str;
+ }
+ }
+
+ hist_field = parse_var_ref(ref_system, ref_event, ref_var);
+ if (hist_field) {
+ hist_data->var_refs[hist_data->n_var_refs] = hist_field;
+ hist_field->var_ref_idx = hist_data->n_var_refs++;
+ return hist_field;
+ }
+
field = parse_field(hist_data, file, str, flags);
if (IS_ERR(field)) {
ret = PTR_ERR(field);
@@ -885,6 +1310,9 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
goto free;
}
+ operand1->read_once = true;
+ operand2->read_once = true;
+
expr->operands[0] = operand1;
expr->operands[1] = operand2;
expr->operator = field_op;
@@ -926,43 +1354,6 @@ static int create_hitcount_val(struct hist_trigger_data *hist_data)
return 0;
}
-static struct hist_field *find_var_field(struct hist_trigger_data *hist_data,
- const char *var_name)
-{
- struct hist_field *hist_field, *found = NULL;
- int i;
-
- for_each_hist_field(i, hist_data) {
- hist_field = hist_data->fields[i];
- if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR &&
- strcmp(hist_field->var.name, var_name) == 0) {
- found = hist_field;
- break;
- }
- }
-
- return found;
-}
-
-static struct hist_field *find_var(struct trace_event_file *file,
- const char *var_name)
-{
- struct hist_trigger_data *hist_data;
- struct event_trigger_data *test;
- struct hist_field *hist_field;
-
- list_for_each_entry_rcu(test, &file->triggers, list) {
- if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
- hist_data = test->private_data;
- hist_field = find_var_field(hist_data, var_name);
- if (hist_field)
- return hist_field;
- }
- }
-
- return NULL;
-}
-
static int create_val_field(struct hist_trigger_data *hist_data,
unsigned int val_idx,
struct trace_event_file *file,
@@ -1119,6 +1510,12 @@ static int create_key_field(struct hist_trigger_data *hist_data,
}
}
+ if (hist_field->flags & HIST_FIELD_FL_VAR_REF) {
+ destroy_hist_field(hist_field, 0);
+ ret = -EINVAL;
+ goto out;
+ }
+
key_size = hist_field->size;
}
@@ -1378,21 +1775,6 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
return 0;
}
-static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
-{
- struct hist_field *key_field;
- unsigned int i;
-
- for_each_hist_key_field(i, hist_data) {
- key_field = hist_data->fields[i];
-
- if (key_field->flags & HIST_FIELD_FL_EXECNAME)
- return true;
- }
-
- return false;
-}
-
static struct hist_trigger_data *
create_hist_data(unsigned int map_bits,
struct hist_trigger_attrs *attrs,
@@ -1418,8 +1800,7 @@ static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
if (ret)
goto free;
- if (need_tracing_map_ops(hist_data))
- map_ops = &hist_trigger_elt_comm_ops;
+ map_ops = &hist_trigger_elt_data_ops;
hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
map_ops, hist_data);
@@ -1433,10 +1814,6 @@ static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
if (ret)
goto free;
- ret = tracing_map_init(hist_data->map);
- if (ret)
- goto free;
-
hist_data->event_file = file;
out:
return hist_data;
@@ -1452,15 +1829,20 @@ static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
struct tracing_map_elt *elt, void *rec,
- struct ring_buffer_event *rbe)
+ struct ring_buffer_event *rbe,
+ u64 *var_ref_vals)
{
+ struct hist_elt_data *elt_data;
struct hist_field *hist_field;
unsigned int i, var_idx;
u64 hist_val;
+ elt_data = elt->private_data;
+ elt_data->var_ref_vals = var_ref_vals;
+
for_each_hist_val_field(i, hist_data) {
hist_field = hist_data->fields[i];
- hist_val = hist_field->fn(hist_field, rbe, rec);
+ hist_val = hist_field->fn(hist_field, elt, rbe, rec);
if (hist_field->flags & HIST_FIELD_FL_VAR) {
var_idx = hist_field->var.idx;
tracing_map_set_var(elt, var_idx, hist_val);
@@ -1473,7 +1855,7 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
for_each_hist_key_field(i, hist_data) {
hist_field = hist_data->fields[i];
if (hist_field->flags & HIST_FIELD_FL_VAR) {
- hist_val = hist_field->fn(hist_field, rbe, rec);
+ hist_val = hist_field->fn(hist_field, elt, rbe, rec);
var_idx = hist_field->var.idx;
tracing_map_set_var(elt, var_idx, hist_val);
}
@@ -1510,10 +1892,11 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
struct hist_trigger_data *hist_data = data->private_data;
bool use_compound_key = (hist_data->n_keys > 1);
unsigned long entries[HIST_STACKTRACE_DEPTH];
+ u64 var_ref_vals[TRACING_MAP_VARS_MAX];
char compound_key[HIST_KEY_SIZE_MAX];
+ struct tracing_map_elt *elt = NULL;
struct stack_trace stacktrace;
struct hist_field *key_field;
- struct tracing_map_elt *elt;
u64 field_contents;
void *key = NULL;
unsigned int i;
@@ -1534,7 +1917,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
key = entries;
} else {
- field_contents = key_field->fn(key_field, rec, rbe);
+ field_contents = key_field->fn(key_field, elt, rbe, rec);
if (key_field->flags & HIST_FIELD_FL_STRING) {
key = (void *)(unsigned long)field_contents;
use_compound_key = true;
@@ -1549,9 +1932,15 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
if (use_compound_key)
key = compound_key;
+ if (hist_data->n_var_refs &&
+ !resolve_var_refs(hist_data, key, var_ref_vals, false))
+ return;
+
elt = tracing_map_insert(hist_data->map, key);
- if (elt)
- hist_trigger_elt_update(hist_data, elt, rec, rbe);
+ if (!elt)
+ return;
+
+ hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals);
}
static void hist_trigger_stacktrace_print(struct seq_file *m,
@@ -1608,7 +1997,8 @@ static void hist_trigger_stacktrace_print(struct seq_file *m,
seq_printf(m, "%s: [%llx] %-55s", field_name,
uval, str);
} else if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
- char *comm = elt->private_data;
+ struct hist_elt_data *elt_data = elt->private_data;
+ char *comm = elt_data->comm;
uval = *(u64 *)(key + key_field->offset);
seq_printf(m, "%s: %-16s[%10llu]", field_name,
@@ -1653,7 +2043,8 @@ static void hist_trigger_stacktrace_print(struct seq_file *m,
field_name = hist_field_name(hist_data->fields[i], 0);
if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR ||
- hist_data->fields[i]->flags & HIST_FIELD_FL_EXPR)
+ hist_data->fields[i]->flags & HIST_FIELD_FL_EXPR ||
+ hist_data->fields[i]->flags & HIST_FIELD_FL_VAR_REF)
continue;
if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) {
@@ -1925,7 +2316,11 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops,
if (!data->ref) {
if (data->name)
del_named_trigger(data);
+
trigger_data_free(data);
+
+ remove_hist_vars(hist_data);
+
destroy_hist_data(hist_data);
}
}
@@ -2139,23 +2534,55 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
goto out;
}
- list_add_rcu(&data->list, &file->triggers);
ret++;
- update_cond_flag(file);
-
if (hist_data->enable_timestamps)
tracing_set_time_stamp_abs(file->tr, true);
+ out:
+ return ret;
+}
+
+static int hist_trigger_enable(struct event_trigger_data *data,
+ struct trace_event_file *file)
+{
+ int ret = 0;
+
+ list_add_rcu(&data->list, &file->triggers);
+
+ update_cond_flag(file);
if (trace_event_trigger_enable_disable(file, 1) < 0) {
list_del_rcu(&data->list);
update_cond_flag(file);
ret--;
}
- out:
+
return ret;
}
+static bool hist_trigger_check_refs(struct event_trigger_data *data,
+ struct trace_event_file *file)
+{
+ struct hist_trigger_data *hist_data = data->private_data;
+ struct event_trigger_data *test, *named_data = NULL;
+
+ if (hist_data->attrs->name)
+ named_data = find_named_trigger(hist_data->attrs->name);
+
+ list_for_each_entry_rcu(test, &file->triggers, list) {
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+ if (!hist_trigger_match(data, test, named_data, false))
+ continue;
+ hist_data = test->private_data;
+ if (check_var_refs(hist_data))
+ return true;
+ break;
+ }
+ }
+
+ return false;
+}
+
static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
struct event_trigger_data *data,
struct trace_event_file *file)
@@ -2186,10 +2613,32 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
tracing_set_time_stamp_abs(file->tr, false);
}
+static bool hist_file_check_refs(struct trace_event_file *file)
+{
+ struct hist_trigger_data *hist_data;
+ struct event_trigger_data *test;
+
+ printk("func: %s\n", __func__);
+
+ list_for_each_entry_rcu(test, &file->triggers, list) {
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+ hist_data = test->private_data;
+ if (check_var_refs(hist_data))
+ return true;
+ break;
+ }
+ }
+
+ return false;
+}
+
static void hist_unreg_all(struct trace_event_file *file)
{
struct event_trigger_data *test, *n;
+ if (hist_file_check_refs(file))
+ return;
+
list_for_each_entry_safe(test, n, &file->triggers, list) {
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
list_del_rcu(&test->list);
@@ -2262,6 +2711,11 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
}
if (remove) {
+ if (hist_trigger_check_refs(trigger_data, file)) {
+ ret = -EBUSY;
+ goto out_free;
+ }
+
cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
ret = 0;
goto out_free;
@@ -2279,14 +2733,33 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
goto out_free;
} else if (ret < 0)
goto out_free;
+
+ if (get_named_trigger_data(trigger_data))
+ goto enable;
+
+ if (has_hist_vars(hist_data))
+ save_hist_vars(hist_data);
+
+ ret = tracing_map_init(hist_data->map);
+ if (ret)
+ goto out_unreg;
+enable:
+ ret = hist_trigger_enable(trigger_data, file);
+ if (ret)
+ goto out_unreg;
+
/* Just return zero, not the number of registered triggers */
ret = 0;
out:
return ret;
+ out_unreg:
+ cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
out_free:
if (cmd_ops->set_filter)
cmd_ops->set_filter(NULL, trigger_data, NULL);
+ remove_hist_vars(hist_data);
+
kfree(trigger_data);
destroy_hist_data(hist_data);
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index d345820..4aefacf 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -919,6 +919,12 @@ void set_named_trigger_data(struct event_trigger_data *data,
data->named_data = named_data;
}
+struct event_trigger_data *
+get_named_trigger_data(struct event_trigger_data *data)
+{
+ return data->named_data;
+}
+
static void
traceon_trigger(struct event_trigger_data *data, void *rec,
struct ring_buffer_event *event)
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 29/32] tracing: Add 'last error' error facility for hist triggers |
| Message-ID | <tWL97-8u4-9@gated-at.bofh.it> |
| In reply to | #1675146 |
With the addition of variables and actions, it's become necessary to
provide more detailed error information to users about syntax errors.
Add a 'last error' facility accessible via the erroring event's 'hist'
file. Reading the hist file after an error will display more detailed
information about what went wrong, if information is available. This
extended error information will be available until the next hist
trigger command for that event.
# echo xxx > /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
echo: write error: Invalid argument
# cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/hist
ERROR: Couldn't yyy: zzz
Last command: xxx
Also add specific error messages for variable and action errors.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
Documentation/trace/events.txt | 19 ++++
kernel/trace/trace_events_hist.c | 181 ++++++++++++++++++++++++++++++++++++---
2 files changed, 188 insertions(+), 12 deletions(-)
diff --git a/Documentation/trace/events.txt b/Documentation/trace/events.txt
index 9717688..f271d87 100644
--- a/Documentation/trace/events.txt
+++ b/Documentation/trace/events.txt
@@ -686,6 +686,25 @@ The following commands are supported:
interpreted as microseconds.
cpu int - the cpu on which the event occurred.
+ Extended error information
+ --------------------------
+
+ For some error conditions encountered when invoking a hist trigger
+ command, extended error information is available via the
+ corresponding event's 'hist' file. Reading the hist file after an
+ error will display more detailed information about what went wrong,
+ if information is available. This extended error information will
+ be available until the next hist trigger command for that event.
+
+ If available for a given error condition, the extended error
+ information and usage takes the following form:
+
+ # echo xxx > /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
+ echo: write error: Invalid argument
+
+ # cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/hist
+ ERROR: Couldn't yyy: zzz
+ Last command: xxx
6.2 'hist' trigger examples
---------------------------
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 799b95e..06049f8 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -288,6 +288,7 @@ struct hist_trigger_data {
struct field_var *max_vars[SYNTH_FIELDS_MAX];
unsigned int n_max_vars;
unsigned int n_max_var_str;
+ char *last_err;
};
struct synth_field {
@@ -332,6 +333,83 @@ struct action_data {
struct hist_field *onmax_var;
};
+
+static char *hist_err_str;
+static char *last_hist_cmd;
+
+static int hist_err_alloc(void)
+{
+ int ret = 0;
+
+ last_hist_cmd = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
+ hist_err_str = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
+ if (!last_hist_cmd || !hist_err_str)
+ ret = -ENOMEM;
+
+ return ret;
+}
+
+static void last_cmd_set(char *str)
+{
+ if (!last_hist_cmd || !str)
+ return;
+
+ if (strlen(last_hist_cmd) > MAX_FILTER_STR_VAL - 1)
+ return;
+
+ strcpy(last_hist_cmd, str);
+}
+
+static void hist_err(char *str, char *var)
+{
+ int maxlen = MAX_FILTER_STR_VAL - 1;
+
+ if (strlen(hist_err_str))
+ return;
+
+ if (!hist_err_str || !str)
+ return;
+
+ if (!var)
+ var = "";
+
+ if (strlen(hist_err_str) + strlen(str) + strlen(var) > maxlen)
+ return;
+
+ strcat(hist_err_str, str);
+ strcat(hist_err_str, var);
+}
+
+static void hist_err_event(char *str, char *system, char *event, char *var)
+{
+ char err[MAX_FILTER_STR_VAL];
+
+ if (system && var)
+ sprintf(err, "%s.%s.%s", system, event, var);
+ else if (system)
+ sprintf(err, "%s.%s", system, event);
+ else
+ strcpy(err, var);
+
+ hist_err(str, err);
+}
+
+static void hist_err_clear(void)
+{
+ if (!hist_err_str)
+ return;
+
+ hist_err_str[0] = '\0';
+}
+
+static bool have_hist_err(void)
+{
+ if (hist_err_str && strlen(hist_err_str))
+ return true;
+
+ return false;
+}
+
static LIST_HEAD(synth_event_list);
static DEFINE_MUTEX(synth_event_mutex);
@@ -1954,12 +2032,21 @@ static struct hist_field *create_var_ref(struct hist_field *var_field)
return ref_field;
}
+static bool is_common_field(char *var_name)
+{
+ if (strncmp(var_name, "$common_timestamp", strlen("$common_timestamp")) == 0)
+ return true;
+
+ return false;
+}
+
static struct hist_field *parse_var_ref(char *system, char *event_name,
char *var_name)
{
struct hist_field *var_field = NULL, *ref_field = NULL;
- if (!var_name || strlen(var_name) < 2 || var_name[0] != '$')
+ if (!var_name || strlen(var_name) < 2 || var_name[0] != '$' ||
+ is_common_field(var_name))
return NULL;
var_name++;
@@ -1968,6 +2055,10 @@ static struct hist_field *parse_var_ref(char *system, char *event_name,
if (var_field)
ref_field = create_var_ref(var_field);
+ if (!ref_field)
+ hist_err_event("Couldn't find variable: $",
+ system, event_name, var_name);
+
return ref_field;
}
@@ -2426,8 +2517,11 @@ static struct trace_event_file *event_file(char *system, char *event_name)
char *cmd;
int ret;
- if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX)
+ if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) {
+ hist_err_event("onmatch: Too many field variables defined: ",
+ system, event_name, field_name);
return ERR_PTR(-EINVAL);
+ }
tr = top_trace_array();
if (!tr)
@@ -2435,13 +2529,18 @@ static struct trace_event_file *event_file(char *system, char *event_name)
file = event_file(system, event_name);
if (IS_ERR(file)) {
+ hist_err_event("onmatch: Event file not found: ",
+ system, event_name, field_name);
ret = PTR_ERR(file);
return ERR_PTR(ret);
}
hist_data = find_compatible_hist(target_hist_data, file);
- if (!hist_data)
+ if (!hist_data) {
+ hist_err_event("onmatch: Matching event histogram not found: ",
+ system, event_name, field_name);
return ERR_PTR(-EINVAL);
+ }
var_hist = kzalloc(sizeof(*var_hist), GFP_KERNEL);
if (!var_hist)
@@ -2489,6 +2588,8 @@ static struct trace_event_file *event_file(char *system, char *event_name)
kfree(cmd);
kfree(var_hist->cmd);
kfree(var_hist);
+ hist_err_event("onmatch: Couldn't create histogram for field: ",
+ system, event_name, field_name);
return ERR_PTR(ret);
}
@@ -2500,6 +2601,8 @@ static struct trace_event_file *event_file(char *system, char *event_name)
kfree(cmd);
kfree(var_hist->cmd);
kfree(var_hist);
+ hist_err_event("onmatch: Couldn't find synthetic variable: ",
+ system, event_name, field_name);
return ERR_PTR(-EINVAL);
}
@@ -2636,18 +2739,21 @@ static struct field_var *create_field_var(struct hist_trigger_data *hist_data,
int ret = 0;
if (hist_data->n_field_vars >= SYNTH_FIELDS_MAX) {
+ hist_err("Too many field variables defined: ", field_name);
ret = -EINVAL;
goto err;
}
val = parse_atom(hist_data, file, field_name, &flags, NULL);
if (IS_ERR(val)) {
+ hist_err("Couldn't parse field variable: ", field_name);
ret = PTR_ERR(val);
goto err;
}
var = create_var(hist_data, file, field_name, val->size, val->type);
if (IS_ERR(var)) {
+ hist_err("Couldn't create or find variable: ", field_name);
kfree(val);
ret = PTR_ERR(var);
goto err;
@@ -2772,14 +2878,18 @@ static int onmax_create(struct hist_trigger_data *hist_data,
int ret = 0;
onmax_var_str = data->onmax_var_str;
- if (onmax_var_str[0] != '$')
+ if (onmax_var_str[0] != '$') {
+ hist_err("onmax: For onmax(x), x must be a variable: ", onmax_var_str);
return -EINVAL;
+ }
onmax_var_str++;
event_name = trace_event_name(call);
var_field = find_target_event_var(hist_data, NULL, NULL, onmax_var_str);
- if (!var_field)
+ if (!var_field) {
+ hist_err("onmax: Couldn't find onmax variable: ", onmax_var_str);
return -EINVAL;
+ }
flags = HIST_FIELD_FL_VAR_REF;
ref_field = create_hist_field(hist_data, NULL, flags, NULL);
@@ -2803,6 +2913,7 @@ static int onmax_create(struct hist_trigger_data *hist_data,
data->max_var_ref_idx = var_ref_idx;
max_var = create_var(hist_data, file, "max", sizeof(u64), "u64");
if (IS_ERR(max_var)) {
+ hist_err("onmax: Couldn't create onmax variable: ", "max");
ret = PTR_ERR(max_var);
goto out;
}
@@ -2815,6 +2926,7 @@ static int onmax_create(struct hist_trigger_data *hist_data,
field_var = create_target_field_var(hist_data, NULL, NULL, param);
if (IS_ERR(field_var)) {
+ hist_err("onmax: Couldn't create field variable: ", param);
ret = PTR_ERR(field_var);
kfree(param);
goto out;
@@ -2847,6 +2959,7 @@ static int parse_action_params(char *params, struct action_data *data)
param = strstrip(param);
if (strlen(param) < 2) {
+ hist_err("Invalid action param: ", param);
ret = -EINVAL;
goto out;
}
@@ -3004,6 +3117,9 @@ static int check_synth_field(struct synth_event *event,
hist_field = find_event_var(system, event, var);
}
+ if (!hist_field)
+ hist_err_event("onmatch: Couldn't find onmatch param: $", system, event, var);
+
return hist_field;
}
@@ -3055,6 +3171,7 @@ static int onmatch_create(struct hist_trigger_data *hist_data,
event = find_synth_event(data->synth_event_name);
if (!event) {
+ hist_err("onmatch: Couldn't find synthetic event: ", data->synth_event_name);
ret = -EINVAL;
goto out;
}
@@ -3094,6 +3211,7 @@ static int onmatch_create(struct hist_trigger_data *hist_data,
ret = -EINVAL;
goto out;
}
+
if (check_synth_field(event, hist_field, field_pos) == 0) {
var_ref = create_var_ref(hist_field);
if (!var_ref) {
@@ -3108,12 +3226,15 @@ static int onmatch_create(struct hist_trigger_data *hist_data,
continue;
}
+ hist_err_event("onmatch: Param type doesn't match synthetic event field type: ",
+ system, event_name, param);
kfree(p);
ret = -EINVAL;
goto out;
}
if (field_pos != event->n_fields) {
+ hist_err("onmatch: Param count doesn't match synthetic event field count: ", event->name);
ret = -EINVAL;
goto out;
}
@@ -3141,31 +3262,44 @@ static struct action_data *onmatch_parse(char *str)
return ERR_PTR(-ENOMEM);
match_event = strsep(&str, ")");
- if (!match_event || !str)
+ if (!match_event || !str) {
+ hist_err("onmatch: Missing closing paren: ", match_event);
goto free;
+ }
match_event_system = strsep(&match_event, ".");
- if (!match_event)
+ if (!match_event) {
+ hist_err("onmatch: Missing subsystem for match event: ", match_event_system);
goto free;
+ }
- if (IS_ERR(event_file(match_event_system, match_event)))
+ if (IS_ERR(event_file(match_event_system, match_event))) {
+ hist_err_event("onmatch: Invalid subsystem or event name: ",
+ match_event_system, match_event, NULL);
goto free;
+ }
data->match_event = kstrdup(match_event, GFP_KERNEL);
data->match_event_system = kstrdup(match_event_system, GFP_KERNEL);
strsep(&str, ".");
- if (!str)
+ if (!str) {
+ hist_err("onmatch: Missing . after onmatch(): ", str);
goto free;
+ }
synth_event_name = strsep(&str, "(");
- if (!synth_event_name || !str)
+ if (!synth_event_name || !str) {
+ hist_err("onmatch: Missing opening paramlist paren: ", synth_event_name);
goto free;
+ }
data->synth_event_name = kstrdup(synth_event_name, GFP_KERNEL);
params = strsep(&str, ")");
- if (!params || !str || (str && strlen(str)))
+ if (!params || !str || (str && strlen(str))) {
+ hist_err("onmatch: Missing closing paramlist paren: ", params);
goto free;
+ }
ret = parse_action_params(params, data);
if (ret)
@@ -3217,6 +3351,7 @@ static int create_val_field(struct hist_trigger_data *hist_data,
if (field_str && var_name) {
if (find_var(file, var_name) &&
!hist_data->remove) {
+ hist_err("Variable already defined: ", var_name);
ret = -EINVAL;
goto out;
}
@@ -3224,6 +3359,7 @@ static int create_val_field(struct hist_trigger_data *hist_data,
flags |= HIST_FIELD_FL_VAR;
hist_data->n_vars++;
if (hist_data->n_vars > TRACING_MAP_VARS_MAX) {
+ hist_err("Too many variables defined: ", var_name);
ret = -EINVAL;
goto out;
}
@@ -3234,6 +3370,7 @@ static int create_val_field(struct hist_trigger_data *hist_data,
field_str = var_name;
var_name = NULL;
} else {
+ hist_err("Malformed assignment: ", var_name);
ret = -EINVAL;
goto out;
}
@@ -3248,6 +3385,7 @@ static int create_val_field(struct hist_trigger_data *hist_data,
hist_field = parse_atom(hist_data, file, field_str,
&flags, var_name);
if (IS_ERR(hist_field)) {
+ hist_err("Unable to parse atom: ", field_str);
ret = PTR_ERR(hist_field);
goto out;
}
@@ -4138,6 +4276,11 @@ static int hist_show(struct seq_file *m, void *v)
hist_trigger_show(m, data, n++);
}
+ if (have_hist_err()) {
+ seq_printf(m, "\nERROR: %s\n", hist_err_str);
+ seq_printf(m, " Last command: %s\n", last_hist_cmd);
+ }
+
out_unlock:
mutex_unlock(&event_mutex);
@@ -4509,6 +4652,7 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
if (named_data) {
if (!hist_trigger_match(data, named_data, named_data,
true)) {
+ hist_err("Named hist trigger doesn't match existing named trigger (includes variables): ", hist_data->attrs->name);
ret = -EINVAL;
goto out;
}
@@ -4528,13 +4672,16 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
test->paused = false;
else if (hist_data->attrs->clear)
hist_clear(test);
- else
+ else {
+ hist_err("Hist trigger already exists", NULL);
ret = -EEXIST;
+ }
goto out;
}
}
new:
if (hist_data->attrs->cont || hist_data->attrs->clear) {
+ hist_err("Can't clear or continue a nonexistent hist trigger", NULL);
ret = -ENOENT;
goto out;
}
@@ -4701,6 +4848,11 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
char *trigger, *p;
int ret = 0;
+ if (glob && strlen(glob)) {
+ last_cmd_set(param);
+ hist_err_clear();
+ }
+
if (!param)
return -EINVAL;
@@ -4804,6 +4956,9 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
/* Just return zero, not the number of registered triggers */
ret = 0;
out:
+ if (ret == 0)
+ hist_err_clear();
+
return ret;
out_unreg:
cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
@@ -5002,6 +5157,8 @@ static __init int trace_events_hist_init(void)
goto err;
}
+ hist_err_alloc();
+
return err;
err:
pr_warn("Could not create tracefs 'synthetic_events' entry\n");
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 04/32] ring-buffer: Redefine the unimplemented RINGBUF_TIME_TIME_STAMP |
| Message-ID | <tWL98-8u4-25@gated-at.bofh.it> |
| In reply to | #1675146 |
RINGBUF_TYPE_TIME_STAMP is defined but not used, and from what I can
gather was reserved for something like an absolute timestamp feature
for the ring buffer, if not a complete replacement of the current
time_delta scheme.
This code redefines RINGBUF_TYPE_TIME_STAMP to implement absolute time
stamps. Another way to look at it is that it essentially forces
extended time_deltas for all events.
The motivation for doing this is to enable time_deltas that aren't
dependent on previous events in the ring buffer, making it feasible to
use the ring_buffer_event timetamps in a more random-access way, for
purposes other than serial event printing.
To set/reset this mode, use tracing_set_timestamp_abs() from the
previous interface patch.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
include/linux/ring_buffer.h | 12 ++---
kernel/trace/ring_buffer.c | 107 +++++++++++++++++++++++++++++++-------------
2 files changed, 83 insertions(+), 36 deletions(-)
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
index 28e3472..74bc276 100644
--- a/include/linux/ring_buffer.h
+++ b/include/linux/ring_buffer.h
@@ -36,10 +36,12 @@ struct ring_buffer_event {
* array[0] = time delta (28 .. 59)
* size = 8 bytes
*
- * @RINGBUF_TYPE_TIME_STAMP: Sync time stamp with external clock
- * array[0] = tv_nsec
- * array[1..2] = tv_sec
- * size = 16 bytes
+ * @RINGBUF_TYPE_TIME_STAMP: Absolute timestamp
+ * Same format as TIME_EXTEND except that the
+ * value is an absolute timestamp, not a delta
+ * event.time_delta contains bottom 27 bits
+ * array[0] = top (28 .. 59) bits
+ * size = 8 bytes
*
* <= @RINGBUF_TYPE_DATA_TYPE_LEN_MAX:
* Data record
@@ -56,12 +58,12 @@ enum ring_buffer_type {
RINGBUF_TYPE_DATA_TYPE_LEN_MAX = 28,
RINGBUF_TYPE_PADDING,
RINGBUF_TYPE_TIME_EXTEND,
- /* FIXME: RINGBUF_TYPE_TIME_STAMP not implemented */
RINGBUF_TYPE_TIME_STAMP,
};
unsigned ring_buffer_event_length(struct ring_buffer_event *event);
void *ring_buffer_event_data(struct ring_buffer_event *event);
+u64 ring_buffer_event_time_stamp(struct ring_buffer_event *event);
/*
* ring_buffer_discard_commit will remove an event that has not
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index f544738..df848f0 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -42,6 +42,8 @@ int ring_buffer_print_entry_header(struct trace_seq *s)
RINGBUF_TYPE_PADDING);
trace_seq_printf(s, "\ttime_extend : type == %d\n",
RINGBUF_TYPE_TIME_EXTEND);
+ trace_seq_printf(s, "\ttime_stamp : type == %d\n",
+ RINGBUF_TYPE_TIME_STAMP);
trace_seq_printf(s, "\tdata max type_len == %d\n",
RINGBUF_TYPE_DATA_TYPE_LEN_MAX);
@@ -147,6 +149,9 @@ enum {
#define skip_time_extend(event) \
((struct ring_buffer_event *)((char *)event + RB_LEN_TIME_EXTEND))
+#define extended_time(event) \
+ (event->type_len >= RINGBUF_TYPE_TIME_EXTEND)
+
static inline int rb_null_event(struct ring_buffer_event *event)
{
return event->type_len == RINGBUF_TYPE_PADDING && !event->time_delta;
@@ -187,10 +192,8 @@ static void rb_event_set_padding(struct ring_buffer_event *event)
return event->array[0] + RB_EVNT_HDR_SIZE;
case RINGBUF_TYPE_TIME_EXTEND:
- return RB_LEN_TIME_EXTEND;
-
case RINGBUF_TYPE_TIME_STAMP:
- return RB_LEN_TIME_STAMP;
+ return RB_LEN_TIME_EXTEND;
case RINGBUF_TYPE_DATA:
return rb_event_data_length(event);
@@ -210,7 +213,7 @@ static void rb_event_set_padding(struct ring_buffer_event *event)
{
unsigned len = 0;
- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) {
+ if (extended_time(event)) {
/* time extends include the data event after it */
len = RB_LEN_TIME_EXTEND;
event = skip_time_extend(event);
@@ -232,7 +235,7 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)
{
unsigned length;
- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND)
+ if (extended_time(event))
event = skip_time_extend(event);
length = rb_event_length(event);
@@ -249,7 +252,7 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)
static __always_inline void *
rb_event_data(struct ring_buffer_event *event)
{
- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND)
+ if (extended_time(event))
event = skip_time_extend(event);
BUG_ON(event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX);
/* If length is in len field, then array[0] has the data */
@@ -276,6 +279,27 @@ void *ring_buffer_event_data(struct ring_buffer_event *event)
#define TS_MASK ((1ULL << TS_SHIFT) - 1)
#define TS_DELTA_TEST (~TS_MASK)
+/**
+ * ring_buffer_event_time_stamp - return the event's extended timestamp
+ * @event: the event to get the timestamp of
+ *
+ * Returns the extended timestamp associated with a data event.
+ * An extended time_stamp is a 64-bit timestamp represented
+ * internally in a special way that makes the best use of space
+ * contained within a ring buffer event. This function decodes
+ * it and maps it to a straight u64 value.
+ */
+u64 ring_buffer_event_time_stamp(struct ring_buffer_event *event)
+{
+ u64 ts;
+
+ ts = event->array[0];
+ ts <<= TS_SHIFT;
+ ts += event->time_delta;
+
+ return ts;
+}
+
/* Flag when events were overwritten */
#define RB_MISSED_EVENTS (1 << 31)
/* Missed count stored at end */
@@ -2220,13 +2244,16 @@ static void rb_inc_iter(struct ring_buffer_iter *iter)
}
/* Slow path, do not inline */
-static noinline struct ring_buffer_event *
-rb_add_time_stamp(struct ring_buffer_event *event, u64 delta)
+static struct noinline ring_buffer_event *
+rb_add_time_stamp(struct ring_buffer_event *event, u64 delta, bool abs)
{
- event->type_len = RINGBUF_TYPE_TIME_EXTEND;
+ if (abs)
+ event->type_len = RINGBUF_TYPE_TIME_STAMP;
+ else
+ event->type_len = RINGBUF_TYPE_TIME_EXTEND;
- /* Not the first event on the page? */
- if (rb_event_index(event)) {
+ /* Not the first event on the page, or not delta? */
+ if (abs || rb_event_index(event)) {
event->time_delta = delta & TS_MASK;
event->array[0] = delta >> TS_SHIFT;
} else {
@@ -2269,7 +2296,9 @@ static inline bool rb_event_is_commit(struct ring_buffer_per_cpu *cpu_buffer,
* add it to the start of the resevered space.
*/
if (unlikely(info->add_timestamp)) {
- event = rb_add_time_stamp(event, delta);
+ bool abs = ring_buffer_time_stamp_abs(cpu_buffer->buffer);
+
+ event = rb_add_time_stamp(event, info->delta, abs);
length -= RB_LEN_TIME_EXTEND;
delta = 0;
}
@@ -2457,7 +2486,7 @@ static __always_inline void rb_end_commit(struct ring_buffer_per_cpu *cpu_buffer
static inline void rb_event_discard(struct ring_buffer_event *event)
{
- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND)
+ if (extended_time(event))
event = skip_time_extend(event);
/* array[0] holds the actual length for the discarded event */
@@ -2488,6 +2517,10 @@ static inline void rb_event_discard(struct ring_buffer_event *event)
{
u64 delta;
+ /* In TIME_STAMP mode, write_stamp is unused, nothing to do */
+ if (event->type_len == RINGBUF_TYPE_TIME_STAMP)
+ return;
+
/*
* The event first in the commit queue updates the
* time stamp.
@@ -2501,9 +2534,7 @@ static inline void rb_event_discard(struct ring_buffer_event *event)
cpu_buffer->write_stamp =
cpu_buffer->commit_page->page->time_stamp;
else if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) {
- delta = event->array[0];
- delta <<= TS_SHIFT;
- delta += event->time_delta;
+ delta = ring_buffer_event_time_stamp(event);
cpu_buffer->write_stamp += delta;
} else
cpu_buffer->write_stamp += event->time_delta;
@@ -2687,7 +2718,7 @@ int ring_buffer_unlock_commit(struct ring_buffer *buffer,
* If this is the first commit on the page, then it has the same
* timestamp as the page itself.
*/
- if (!tail)
+ if (!tail && !ring_buffer_time_stamp_abs(cpu_buffer->buffer))
info->delta = 0;
/* See if we shot pass the end of this buffer page */
@@ -2765,8 +2796,11 @@ int ring_buffer_unlock_commit(struct ring_buffer *buffer,
/* make sure this diff is calculated here */
barrier();
- /* Did the write stamp get updated already? */
- if (likely(info.ts >= cpu_buffer->write_stamp)) {
+ if (ring_buffer_time_stamp_abs(buffer)) {
+ info.delta = info.ts;
+ rb_handle_timestamp(cpu_buffer, &info);
+ } else /* Did the write stamp get updated already? */
+ if (likely(info.ts >= cpu_buffer->write_stamp)) {
info.delta = diff;
if (unlikely(test_time_stamp(info.delta)))
rb_handle_timestamp(cpu_buffer, &info);
@@ -3448,14 +3482,12 @@ int ring_buffer_iter_empty(struct ring_buffer_iter *iter)
return;
case RINGBUF_TYPE_TIME_EXTEND:
- delta = event->array[0];
- delta <<= TS_SHIFT;
- delta += event->time_delta;
+ delta = ring_buffer_event_time_stamp(event);
cpu_buffer->read_stamp += delta;
return;
case RINGBUF_TYPE_TIME_STAMP:
- /* FIXME: not implemented */
+ /* In TIME_STAMP mode, write_stamp is unused, nothing to do */
return;
case RINGBUF_TYPE_DATA:
@@ -3479,14 +3511,12 @@ int ring_buffer_iter_empty(struct ring_buffer_iter *iter)
return;
case RINGBUF_TYPE_TIME_EXTEND:
- delta = event->array[0];
- delta <<= TS_SHIFT;
- delta += event->time_delta;
+ delta = ring_buffer_event_time_stamp(event);
iter->read_stamp += delta;
return;
case RINGBUF_TYPE_TIME_STAMP:
- /* FIXME: not implemented */
+ /* In TIME_STAMP mode, write_stamp is unused, nothing to do */
return;
case RINGBUF_TYPE_DATA:
@@ -3710,6 +3740,8 @@ static int rb_lost_events(struct ring_buffer_per_cpu *cpu_buffer)
struct buffer_page *reader;
int nr_loops = 0;
+ if (ts)
+ *ts = 0;
again:
/*
* We repeat when a time extend is encountered.
@@ -3746,12 +3778,17 @@ static int rb_lost_events(struct ring_buffer_per_cpu *cpu_buffer)
goto again;
case RINGBUF_TYPE_TIME_STAMP:
- /* FIXME: not implemented */
+ if (ts) {
+ *ts = ring_buffer_event_time_stamp(event);
+ ring_buffer_normalize_time_stamp(cpu_buffer->buffer,
+ cpu_buffer->cpu, ts);
+ }
+ /* Internal data, OK to advance */
rb_advance_reader(cpu_buffer);
goto again;
case RINGBUF_TYPE_DATA:
- if (ts) {
+ if (ts && !(*ts)) {
*ts = cpu_buffer->read_stamp + event->time_delta;
ring_buffer_normalize_time_stamp(cpu_buffer->buffer,
cpu_buffer->cpu, ts);
@@ -3776,6 +3813,9 @@ static int rb_lost_events(struct ring_buffer_per_cpu *cpu_buffer)
struct ring_buffer_event *event;
int nr_loops = 0;
+ if (ts)
+ *ts = 0;
+
cpu_buffer = iter->cpu_buffer;
buffer = cpu_buffer->buffer;
@@ -3828,12 +3868,17 @@ static int rb_lost_events(struct ring_buffer_per_cpu *cpu_buffer)
goto again;
case RINGBUF_TYPE_TIME_STAMP:
- /* FIXME: not implemented */
+ if (ts) {
+ *ts = ring_buffer_event_time_stamp(event);
+ ring_buffer_normalize_time_stamp(cpu_buffer->buffer,
+ cpu_buffer->cpu, ts);
+ }
+ /* Internal data, OK to advance */
rb_advance_iter(iter);
goto again;
case RINGBUF_TYPE_DATA:
- if (ts) {
+ if (ts && !(*ts)) {
*ts = iter->read_stamp + event->time_delta;
ring_buffer_normalize_time_stamp(buffer,
cpu_buffer->cpu, ts);
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | "Joel Fernandes (Google)" <joel.opensrc@gmail.com> |
|---|---|
| Date | 2017-07-02 11:00 +0200 |
| Subject | Re: [PATCH 04/32] ring-buffer: Redefine the unimplemented RINGBUF_TIME_TIME_STAMP |
| Message-ID | <tYITv-7uK-7@gated-at.bofh.it> |
| In reply to | #1675153 |
On Mon, Jun 26, 2017 at 3:49 PM, Tom Zanussi
<tom.zanussi@linux.intel.com> wrote:
> RINGBUF_TYPE_TIME_STAMP is defined but not used, and from what I can
> gather was reserved for something like an absolute timestamp feature
> for the ring buffer, if not a complete replacement of the current
> time_delta scheme.
>
> This code redefines RINGBUF_TYPE_TIME_STAMP to implement absolute time
> stamps. Another way to look at it is that it essentially forces
> extended time_deltas for all events.
>
> The motivation for doing this is to enable time_deltas that aren't
> dependent on previous events in the ring buffer, making it feasible to
> use the ring_buffer_event timetamps in a more random-access way, for
> purposes other than serial event printing.
>
> To set/reset this mode, use tracing_set_timestamp_abs() from the
> previous interface patch.
>
> Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
> ---
> include/linux/ring_buffer.h | 12 ++---
> kernel/trace/ring_buffer.c | 107 +++++++++++++++++++++++++++++++-------------
> 2 files changed, 83 insertions(+), 36 deletions(-)
>
> diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
> index 28e3472..74bc276 100644
> --- a/include/linux/ring_buffer.h
> +++ b/include/linux/ring_buffer.h
> @@ -36,10 +36,12 @@ struct ring_buffer_event {
> * array[0] = time delta (28 .. 59)
> * size = 8 bytes
> *
> - * @RINGBUF_TYPE_TIME_STAMP: Sync time stamp with external clock
> - * array[0] = tv_nsec
> - * array[1..2] = tv_sec
> - * size = 16 bytes
> + * @RINGBUF_TYPE_TIME_STAMP: Absolute timestamp
> + * Same format as TIME_EXTEND except that the
> + * value is an absolute timestamp, not a delta
> + * event.time_delta contains bottom 27 bits
> + * array[0] = top (28 .. 59) bits
> + * size = 8 bytes
> *
Let's also change it here in ring_buffer.c?
enum {
RB_LEN_TIME_EXTEND = 8,
RB_LEN_TIME_STAMP = 16, <- change to 8
};
> * <= @RINGBUF_TYPE_DATA_TYPE_LEN_MAX:
> * Data record
> @@ -56,12 +58,12 @@ enum ring_buffer_type {
> RINGBUF_TYPE_DATA_TYPE_LEN_MAX = 28,
> RINGBUF_TYPE_PADDING,
> RINGBUF_TYPE_TIME_EXTEND,
> - /* FIXME: RINGBUF_TYPE_TIME_STAMP not implemented */
> RINGBUF_TYPE_TIME_STAMP,
> };
>
> unsigned ring_buffer_event_length(struct ring_buffer_event *event);
> void *ring_buffer_event_data(struct ring_buffer_event *event);
> +u64 ring_buffer_event_time_stamp(struct ring_buffer_event *event);
>
> /*
> * ring_buffer_discard_commit will remove an event that has not
> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> index f544738..df848f0 100644
> --- a/kernel/trace/ring_buffer.c
> +++ b/kernel/trace/ring_buffer.c
> @@ -42,6 +42,8 @@ int ring_buffer_print_entry_header(struct trace_seq *s)
> RINGBUF_TYPE_PADDING);
> trace_seq_printf(s, "\ttime_extend : type == %d\n",
> RINGBUF_TYPE_TIME_EXTEND);
> + trace_seq_printf(s, "\ttime_stamp : type == %d\n",
> + RINGBUF_TYPE_TIME_STAMP);
> trace_seq_printf(s, "\tdata max type_len == %d\n",
> RINGBUF_TYPE_DATA_TYPE_LEN_MAX);
>
> @@ -147,6 +149,9 @@ enum {
> #define skip_time_extend(event) \
> ((struct ring_buffer_event *)((char *)event + RB_LEN_TIME_EXTEND))
>
> +#define extended_time(event) \
> + (event->type_len >= RINGBUF_TYPE_TIME_EXTEND)
> +
> static inline int rb_null_event(struct ring_buffer_event *event)
> {
> return event->type_len == RINGBUF_TYPE_PADDING && !event->time_delta;
> @@ -187,10 +192,8 @@ static void rb_event_set_padding(struct ring_buffer_event *event)
> return event->array[0] + RB_EVNT_HDR_SIZE;
>
> case RINGBUF_TYPE_TIME_EXTEND:
> - return RB_LEN_TIME_EXTEND;
> -
> case RINGBUF_TYPE_TIME_STAMP:
> - return RB_LEN_TIME_STAMP;
> + return RB_LEN_TIME_EXTEND;
And then this change can be dropped. Or a new single enum can be
defined for both TIME_EXTEND and TIME_STAMP and then used.
thanks,
-Joel
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 06/32] tracing: Add ring buffer event param to hist field functions |
| Message-ID | <tWL98-8u4-27@gated-at.bofh.it> |
| In reply to | #1675146 |
Some events such as timestamps require access to a ring_buffer_event
struct; add a param so that hist field functions can access that.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace_events_hist.c | 39 ++++++++++++++++++++++++---------------
1 file changed, 24 insertions(+), 15 deletions(-)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 655c412..613ff05 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -26,7 +26,8 @@
struct hist_field;
-typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event);
+typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event,
+ struct ring_buffer_event *rbe);
#define HIST_FIELD_OPERANDS_MAX 2
@@ -40,24 +41,28 @@ struct hist_field {
struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
};
-static u64 hist_field_none(struct hist_field *field, void *event)
+static u64 hist_field_none(struct hist_field *field, void *event,
+ struct ring_buffer_event *rbe)
{
return 0;
}
-static u64 hist_field_counter(struct hist_field *field, void *event)
+static u64 hist_field_counter(struct hist_field *field, void *event,
+ struct ring_buffer_event *rbe)
{
return 1;
}
-static u64 hist_field_string(struct hist_field *hist_field, void *event)
+static u64 hist_field_string(struct hist_field *hist_field, void *event,
+ struct ring_buffer_event *rbe)
{
char *addr = (char *)(event + hist_field->field->offset);
return (u64)(unsigned long)addr;
}
-static u64 hist_field_dynstring(struct hist_field *hist_field, void *event)
+static u64 hist_field_dynstring(struct hist_field *hist_field, void *event,
+ struct ring_buffer_event *rbe)
{
u32 str_item = *(u32 *)(event + hist_field->field->offset);
int str_loc = str_item & 0xffff;
@@ -66,24 +71,28 @@ static u64 hist_field_dynstring(struct hist_field *hist_field, void *event)
return (u64)(unsigned long)addr;
}
-static u64 hist_field_pstring(struct hist_field *hist_field, void *event)
+static u64 hist_field_pstring(struct hist_field *hist_field, void *event,
+ struct ring_buffer_event *rbe)
{
char **addr = (char **)(event + hist_field->field->offset);
return (u64)(unsigned long)*addr;
}
-static u64 hist_field_log2(struct hist_field *hist_field, void *event)
+static u64 hist_field_log2(struct hist_field *hist_field, void *event,
+ struct ring_buffer_event *rbe)
{
struct hist_field *operand = hist_field->operands[0];
- u64 val = operand->fn(operand, event);
+ u64 val = operand->fn(operand, event, rbe);
return (u64) ilog2(roundup_pow_of_two(val));
}
#define DEFINE_HIST_FIELD_FN(type) \
-static u64 hist_field_##type(struct hist_field *hist_field, void *event)\
+ static u64 hist_field_##type(struct hist_field *hist_field, \
+ void *event, \
+ struct ring_buffer_event *rbe) \
{ \
type *addr = (type *)(event + hist_field->field->offset); \
\
@@ -883,8 +892,8 @@ static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
}
static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
- struct tracing_map_elt *elt,
- void *rec)
+ struct tracing_map_elt *elt, void *rec,
+ struct ring_buffer_event *rbe)
{
struct hist_field *hist_field;
unsigned int i;
@@ -892,7 +901,7 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
for_each_hist_val_field(i, hist_data) {
hist_field = hist_data->fields[i];
- hist_val = hist_field->fn(hist_field, rec);
+ hist_val = hist_field->fn(hist_field, rec, rbe);
tracing_map_update_sum(elt, i, hist_val);
}
}
@@ -922,7 +931,7 @@ static inline void add_to_key(char *compound_key, void *key,
}
static void event_hist_trigger(struct event_trigger_data *data, void *rec,
- struct ring_buffer_event *event)
+ struct ring_buffer_event *rbe)
{
struct hist_trigger_data *hist_data = data->private_data;
bool use_compound_key = (hist_data->n_keys > 1);
@@ -951,7 +960,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
key = entries;
} else {
- field_contents = key_field->fn(key_field, rec);
+ field_contents = key_field->fn(key_field, rec, rbe);
if (key_field->flags & HIST_FIELD_FL_STRING) {
key = (void *)(unsigned long)field_contents;
use_compound_key = true;
@@ -968,7 +977,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
elt = tracing_map_insert(hist_data->map, key);
if (elt)
- hist_trigger_elt_update(hist_data, elt, rec);
+ hist_trigger_elt_update(hist_data, elt, rec, rbe);
}
static void hist_trigger_stacktrace_print(struct seq_file *m,
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 16/32] tracing: Add variable support to hist triggers |
| Message-ID | <tWL97-8u4-11@gated-at.bofh.it> |
| In reply to | #1675146 |
Add support for saving the value of a current event's event field by
assigning it to a variable that can be read by a subsequent event.
The basic syntax for saving a variable is to simply prefix a unique
variable name not corresponding to any keyword along with an '=' sign
to any event field.
Both keys and values can be saved and retrieved in this way:
# echo 'hist:keys=next_pid:vals=ts0=common_timestamp ...
# echo 'hist:key=timer_pid=common_pid ...'
If a variable isn't a key variable or prefixed with 'vals=', the
associated event field will be saved in a variable but won't be summed
as a value:
# echo 'hist:keys=next_pid:ts1=common_timestamp:...
Multiple variables can be assigned at the same time:
# echo 'hist:keys=pid:vals=ts0=common_timestamp,b=field1,field2 ...
Multiple (or single) variables can also be assigned at the same time
using separate assignments:
# echo 'hist:keys=pid:vals=ts0=common_timestamp:b=field1:c=field2 ...
Variables set as above can be used by being referenced from another
event, as described in a subsequent patch.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace_events_hist.c | 299 ++++++++++++++++++++++++++++++++++-----
1 file changed, 264 insertions(+), 35 deletions(-)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 5c351d7..75cac76 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -30,6 +30,13 @@ typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event,
struct ring_buffer_event *rbe);
#define HIST_FIELD_OPERANDS_MAX 2
+#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
+
+struct hist_var {
+ char *name;
+ struct hist_trigger_data *hist_data;
+ unsigned int idx;
+};
struct hist_field {
struct ftrace_event_field *field;
@@ -40,6 +47,7 @@ struct hist_field {
unsigned int is_signed;
struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
struct hist_trigger_data *hist_data;
+ struct hist_var var;
};
static u64 hist_field_none(struct hist_field *field, void *event,
@@ -138,6 +146,8 @@ enum hist_field_flags {
HIST_FIELD_FL_LOG2 = 512,
HIST_FIELD_FL_TIMESTAMP = 1024,
HIST_FIELD_FL_TIMESTAMP_USECS = 2048,
+ HIST_FIELD_FL_VAR = 4096,
+ HIST_FIELD_FL_VAR_ONLY = 8192,
};
struct hist_trigger_attrs {
@@ -150,13 +160,18 @@ struct hist_trigger_attrs {
bool clear;
bool ts_in_usecs;
unsigned int map_bits;
+
+ char *assignment_str[TRACING_MAP_VARS_MAX];
+ unsigned int n_assignments;
};
struct hist_trigger_data {
- struct hist_field *fields[TRACING_MAP_FIELDS_MAX];
+ struct hist_field *fields[HIST_FIELDS_MAX];
unsigned int n_vals;
unsigned int n_keys;
unsigned int n_fields;
+ unsigned int n_vars;
+ unsigned int n_var_only;
unsigned int key_size;
struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX];
unsigned int n_sort_keys;
@@ -164,6 +179,7 @@ struct hist_trigger_data {
struct hist_trigger_attrs *attrs;
struct tracing_map *map;
bool enable_timestamps;
+ bool remove;
};
static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
@@ -262,9 +278,14 @@ static int parse_map_size(char *str)
static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
{
+ unsigned int i;
+
if (!attrs)
return;
+ for (i = 0; i < attrs->n_assignments; i++)
+ kfree(attrs->assignment_str[i]);
+
kfree(attrs->name);
kfree(attrs->sort_key_str);
kfree(attrs->keys_str);
@@ -295,8 +316,22 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
goto out;
}
attrs->map_bits = map_bits;
- } else
- ret = -EINVAL;
+ } else {
+ char *assignment;
+
+ if (attrs->n_assignments == TRACING_MAP_VARS_MAX) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ assignment = kstrdup(str, GFP_KERNEL);
+ if (!assignment) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ attrs->assignment_str[attrs->n_assignments++] = assignment;
+ }
out:
return ret;
}
@@ -423,12 +458,15 @@ static void destroy_hist_field(struct hist_field *hist_field,
for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++)
destroy_hist_field(hist_field->operands[i], ++level);
+ kfree(hist_field->var.name);
+
kfree(hist_field);
}
static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
struct ftrace_event_field *field,
- unsigned long flags)
+ unsigned long flags,
+ char *var_name)
{
struct hist_field *hist_field;
@@ -454,7 +492,7 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
if (flags & HIST_FIELD_FL_LOG2) {
unsigned long fl = flags & ~HIST_FIELD_FL_LOG2;
hist_field->fn = hist_field_log2;
- hist_field->operands[0] = create_hist_field(hist_data, field, fl);
+ hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL);
hist_field->size = hist_field->operands[0]->size;
goto out;
}
@@ -489,14 +527,23 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
hist_field->field = field;
hist_field->flags = flags;
+ if (var_name) {
+ hist_field->var.name = kstrdup(var_name, GFP_KERNEL);
+ if (!hist_field->var.name)
+ goto free;
+ }
+
return hist_field;
+ free:
+ destroy_hist_field(hist_field, 0);
+ return NULL;
}
static void destroy_hist_fields(struct hist_trigger_data *hist_data)
{
unsigned int i;
- for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) {
+ for (i = 0; i < HIST_FIELDS_MAX; i++) {
if (hist_data->fields[i]) {
destroy_hist_field(hist_data->fields[i], 0);
hist_data->fields[i] = NULL;
@@ -507,11 +554,12 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
static int create_hitcount_val(struct hist_trigger_data *hist_data)
{
hist_data->fields[HITCOUNT_IDX] =
- create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT);
+ create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT, NULL);
if (!hist_data->fields[HITCOUNT_IDX])
return -ENOMEM;
hist_data->n_vals++;
+ hist_data->n_fields++;
if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX))
return -EINVAL;
@@ -519,19 +567,81 @@ static int create_hitcount_val(struct hist_trigger_data *hist_data)
return 0;
}
+static struct hist_field *find_var_field(struct hist_trigger_data *hist_data,
+ const char *var_name)
+{
+ struct hist_field *hist_field, *found = NULL;
+ int i;
+
+ for_each_hist_field(i, hist_data) {
+ hist_field = hist_data->fields[i];
+ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR &&
+ strcmp(hist_field->var.name, var_name) == 0) {
+ found = hist_field;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static struct hist_field *find_var(struct trace_event_file *file,
+ const char *var_name)
+{
+ struct hist_trigger_data *hist_data;
+ struct event_trigger_data *test;
+ struct hist_field *hist_field;
+
+ list_for_each_entry_rcu(test, &file->triggers, list) {
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+ hist_data = test->private_data;
+ hist_field = find_var_field(hist_data, var_name);
+ if (hist_field)
+ return hist_field;
+ }
+ }
+
+ return NULL;
+}
+
static int create_val_field(struct hist_trigger_data *hist_data,
unsigned int val_idx,
struct trace_event_file *file,
- char *field_str)
+ char *field_str, bool var_only)
{
struct ftrace_event_field *field = NULL;
+ char *field_name, *var_name;
unsigned long flags = 0;
- char *field_name;
int ret = 0;
- if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX))
+ if (WARN_ON(!var_only && val_idx >= TRACING_MAP_VALS_MAX))
return -EINVAL;
+ var_name = strsep(&field_str, "=");
+ if (field_str && var_name) {
+ if (find_var(file, var_name) &&
+ !hist_data->remove) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ flags |= HIST_FIELD_FL_VAR;
+ hist_data->n_vars++;
+ if (hist_data->n_vars > TRACING_MAP_VARS_MAX) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (var_only)
+ flags |= HIST_FIELD_FL_VAR_ONLY;
+ } else if (!var_only && var_name != NULL && field_str == NULL) {
+ field_str = var_name;
+ var_name = NULL;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
field_name = strsep(&field_str, ".");
if (field_str) {
if (strcmp(field_str, "hex") == 0)
@@ -553,15 +663,19 @@ static int create_val_field(struct hist_trigger_data *hist_data,
}
}
- hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags);
+ hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags, var_name);
if (!hist_data->fields[val_idx]) {
ret = -ENOMEM;
goto out;
}
++hist_data->n_vals;
+ ++hist_data->n_fields;
- if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX))
+ if (hist_data->fields[val_idx]->flags & HIST_FIELD_FL_VAR_ONLY)
+ hist_data->n_var_only++;
+
+ if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX))
ret = -EINVAL;
out:
return ret;
@@ -571,7 +685,7 @@ static int create_val_fields(struct hist_trigger_data *hist_data,
struct trace_event_file *file)
{
char *fields_str, *field_str;
- unsigned int i, j;
+ unsigned int i, j = 1;
int ret;
ret = create_hitcount_val(hist_data);
@@ -591,12 +705,15 @@ static int create_val_fields(struct hist_trigger_data *hist_data,
field_str = strsep(&fields_str, ",");
if (!field_str)
break;
+
if (strcmp(field_str, "hitcount") == 0)
continue;
- ret = create_val_field(hist_data, j++, file, field_str);
+
+ ret = create_val_field(hist_data, j++, file, field_str, false);
if (ret)
goto out;
}
+
if (fields_str && (strcmp(fields_str, "hitcount") != 0))
ret = -EINVAL;
out:
@@ -610,18 +727,32 @@ static int create_key_field(struct hist_trigger_data *hist_data,
char *field_str)
{
struct ftrace_event_field *field = NULL;
+ struct hist_field *hist_field = NULL;
unsigned long flags = 0;
unsigned int key_size;
+ char *var_name;
int ret = 0;
- if (WARN_ON(key_idx >= TRACING_MAP_FIELDS_MAX))
+ if (WARN_ON(key_idx >= HIST_FIELDS_MAX))
return -EINVAL;
flags |= HIST_FIELD_FL_KEY;
+ var_name = strsep(&field_str, "=");
+ if (field_str) {
+ if (find_var(file, var_name) &&
+ !hist_data->remove)
+ return -EINVAL;
+ flags |= HIST_FIELD_FL_VAR;
+ } else {
+ field_str = var_name;
+ var_name = NULL;
+ }
+
if (strcmp(field_str, "stacktrace") == 0) {
flags |= HIST_FIELD_FL_STACKTRACE;
key_size = sizeof(unsigned long) * HIST_STACKTRACE_DEPTH;
+ hist_field = create_hist_field(hist_data, NULL, flags, var_name);
} else {
char *field_name = strsep(&field_str, ".");
@@ -667,7 +798,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
}
}
- hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags);
+ hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags, var_name);
if (!hist_data->fields[key_idx]) {
ret = -ENOMEM;
goto out;
@@ -683,6 +814,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
}
hist_data->n_keys++;
+ hist_data->n_fields++;
if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX))
return -EINVAL;
@@ -726,6 +858,29 @@ static int create_key_fields(struct hist_trigger_data *hist_data,
return ret;
}
+static int create_var_fields(struct hist_trigger_data *hist_data,
+ struct trace_event_file *file)
+{
+ unsigned int i, j, k = hist_data->n_vals;
+ char *str, *field_str;
+ int ret = 0;
+
+ for (i = 0; i < hist_data->attrs->n_assignments; i++) {
+ str = hist_data->attrs->assignment_str[i];
+
+ for (j = 0; j < TRACING_MAP_VARS_MAX; j++) {
+ field_str = strsep(&str, ",");
+ if (!field_str)
+ break;
+ ret = create_val_field(hist_data, k++, file, field_str, true);
+ if (ret)
+ goto out;
+ }
+ }
+ out:
+ return ret;
+}
+
static int create_hist_fields(struct hist_trigger_data *hist_data,
struct trace_event_file *file)
{
@@ -735,11 +890,13 @@ static int create_hist_fields(struct hist_trigger_data *hist_data,
if (ret)
goto out;
- ret = create_key_fields(hist_data, file);
+ ret = create_var_fields(hist_data, file);
if (ret)
goto out;
- hist_data->n_fields = hist_data->n_vals + hist_data->n_keys;
+ ret = create_key_fields(hist_data, file);
+ if (ret)
+ goto out;
out:
return ret;
}
@@ -763,7 +920,7 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
char *fields_str = hist_data->attrs->sort_key_str;
struct tracing_map_sort_key *sort_key;
int descending, ret = 0;
- unsigned int i, j;
+ unsigned int i, j, k;
hist_data->n_sort_keys = 1; /* we always have at least one, hitcount */
@@ -811,13 +968,21 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
continue;
}
- for (j = 1; j < hist_data->n_fields; j++) {
+ for (j = 1, k = 1; j < hist_data->n_fields; j++) {
+ unsigned idx;
+
hist_field = hist_data->fields[j];
+ if (hist_field->flags & HIST_FIELD_FL_VAR_ONLY)
+ continue;
+
+ idx = k++;
+
test_name = hist_field_name(hist_field, 0);
+
if (test_name == NULL)
continue;
if (strcmp(field_name, test_name) == 0) {
- sort_key->field_idx = j;
+ sort_key->field_idx = idx;
descending = is_descending(field_str);
if (descending < 0) {
ret = descending;
@@ -832,6 +997,7 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
break;
}
}
+
hist_data->n_sort_keys = i;
out:
return ret;
@@ -872,12 +1038,19 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
idx = tracing_map_add_key_field(map,
hist_field->offset,
cmp_fn);
-
- } else
+ } else if (!(hist_field->flags & HIST_FIELD_FL_VAR))
idx = tracing_map_add_sum_field(map);
if (idx < 0)
return idx;
+
+ if (hist_field->flags & HIST_FIELD_FL_VAR) {
+ idx = tracing_map_add_var(map);
+ if (idx < 0)
+ return idx;
+ hist_field->var.idx = idx;
+ hist_field->var.hist_data = hist_data;
+ }
}
return 0;
@@ -901,7 +1074,8 @@ static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
static struct hist_trigger_data *
create_hist_data(unsigned int map_bits,
struct hist_trigger_attrs *attrs,
- struct trace_event_file *file)
+ struct trace_event_file *file,
+ bool remove)
{
const struct tracing_map_ops *map_ops = NULL;
struct hist_trigger_data *hist_data;
@@ -912,6 +1086,7 @@ static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
return ERR_PTR(-ENOMEM);
hist_data->attrs = attrs;
+ hist_data->remove = remove;
ret = create_hist_fields(hist_data, file);
if (ret)
@@ -958,14 +1133,29 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
struct ring_buffer_event *rbe)
{
struct hist_field *hist_field;
- unsigned int i;
+ unsigned int i, var_idx;
u64 hist_val;
for_each_hist_val_field(i, hist_data) {
hist_field = hist_data->fields[i];
- hist_val = hist_field->fn(hist_field, rec, rbe);
+ hist_val = hist_field->fn(hist_field, rbe, rec);
+ if (hist_field->flags & HIST_FIELD_FL_VAR) {
+ var_idx = hist_field->var.idx;
+ tracing_map_set_var(elt, var_idx, hist_val);
+ if (hist_field->flags & HIST_FIELD_FL_VAR_ONLY)
+ continue;
+ }
tracing_map_update_sum(elt, i, hist_val);
}
+
+ for_each_hist_key_field(i, hist_data) {
+ hist_field = hist_data->fields[i];
+ if (hist_field->flags & HIST_FIELD_FL_VAR) {
+ hist_val = hist_field->fn(hist_field, rbe, rec);
+ var_idx = hist_field->var.idx;
+ tracing_map_set_var(elt, var_idx, hist_val);
+ }
+ }
}
static inline void add_to_key(char *compound_key, void *key,
@@ -1140,6 +1330,9 @@ static void hist_trigger_stacktrace_print(struct seq_file *m,
for (i = 1; i < hist_data->n_vals; i++) {
field_name = hist_field_name(hist_data->fields[i], 0);
+ if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR)
+ continue;
+
if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) {
seq_printf(m, " %s: %10llx", field_name,
tracing_map_read_sum(elt, i));
@@ -1263,6 +1456,9 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
{
const char *field_name = hist_field_name(hist_field, 0);
+ if (hist_field->var.name)
+ seq_printf(m, "%s=", hist_field->var.name);
+
if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP)
seq_puts(m, "$common_timestamp");
else if (field_name)
@@ -1281,7 +1477,8 @@ static int event_hist_trigger_print(struct seq_file *m,
struct event_trigger_data *data)
{
struct hist_trigger_data *hist_data = data->private_data;
- struct hist_field *key_field;
+ bool have_var_only = false;
+ struct hist_field *field;
unsigned int i;
seq_puts(m, "hist:");
@@ -1292,25 +1489,47 @@ static int event_hist_trigger_print(struct seq_file *m,
seq_puts(m, "keys=");
for_each_hist_key_field(i, hist_data) {
- key_field = hist_data->fields[i];
+ field = hist_data->fields[i];
if (i > hist_data->n_vals)
seq_puts(m, ",");
- if (key_field->flags & HIST_FIELD_FL_STACKTRACE)
+ if (field->flags & HIST_FIELD_FL_STACKTRACE)
seq_puts(m, "stacktrace");
else
- hist_field_print(m, key_field);
+ hist_field_print(m, field);
}
seq_puts(m, ":vals=");
for_each_hist_val_field(i, hist_data) {
+ field = hist_data->fields[i];
+ if (field->flags & HIST_FIELD_FL_VAR_ONLY) {
+ have_var_only = true;
+ continue;
+ }
+
if (i == HITCOUNT_IDX)
seq_puts(m, "hitcount");
else {
seq_puts(m, ",");
- hist_field_print(m, hist_data->fields[i]);
+ hist_field_print(m, field);
+ }
+ }
+
+ if (have_var_only) {
+ unsigned int n = 0;
+
+ seq_puts(m, ":");
+
+ for_each_hist_val_field(i, hist_data) {
+ field = hist_data->fields[i];
+
+ if (field->flags & HIST_FIELD_FL_VAR_ONLY) {
+ if (n++)
+ seq_puts(m, ",");
+ hist_field_print(m, field);
+ }
}
}
@@ -1318,7 +1537,10 @@ static int event_hist_trigger_print(struct seq_file *m,
for (i = 0; i < hist_data->n_sort_keys; i++) {
struct tracing_map_sort_key *sort_key;
- unsigned int idx;
+ unsigned int idx, first_key_idx;
+
+ /* skip VAR_ONLY vals */
+ first_key_idx = hist_data->n_vals - hist_data->n_var_only;
sort_key = &hist_data->sort_keys[i];
idx = sort_key->field_idx;
@@ -1331,8 +1553,11 @@ static int event_hist_trigger_print(struct seq_file *m,
if (idx == HITCOUNT_IDX)
seq_puts(m, "hitcount");
- else
+ else {
+ if (idx >= first_key_idx)
+ idx += hist_data->n_var_only;
hist_field_print(m, hist_data->fields[idx]);
+ }
if (sort_key->descending)
seq_puts(m, ".descending");
@@ -1656,12 +1881,16 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
struct hist_trigger_attrs *attrs;
struct event_trigger_ops *trigger_ops;
struct hist_trigger_data *hist_data;
+ bool remove = false;
char *trigger;
int ret = 0;
if (!param)
return -EINVAL;
+ if (glob[0] == '!')
+ remove = true;
+
/* separate the trigger from the filter (k:v [if filter]) */
trigger = strsep(¶m, " \t");
if (!trigger)
@@ -1674,7 +1903,7 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
if (attrs->map_bits)
hist_trigger_bits = attrs->map_bits;
- hist_data = create_hist_data(hist_trigger_bits, attrs, file);
+ hist_data = create_hist_data(hist_trigger_bits, attrs, file, remove);
if (IS_ERR(hist_data)) {
destroy_hist_trigger_attrs(attrs);
return PTR_ERR(hist_data);
@@ -1703,7 +1932,7 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
goto out_free;
}
- if (glob[0] == '!') {
+ if (remove) {
cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
ret = 0;
goto out_free;
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 20/32] tracing: Add support for dynamic tracepoints |
| Message-ID | <tWL97-8u4-13@gated-at.bofh.it> |
| In reply to | #1675146 |
The tracepoint infrastructure assumes statically-defined tracepoints
and uses static_keys for tracepoint enablement. In order to define
tracepoints on the fly, we need to have a dynamic counterpart.
Add a dynamic_tracepoint_probe_register() and a dynamic param onto
tracepoint_probe_unregister() for this purpose.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
include/linux/tracepoint.h | 11 +++++++----
kernel/trace/trace_events.c | 4 ++--
kernel/tracepoint.c | 42 ++++++++++++++++++++++++++++++------------
3 files changed, 39 insertions(+), 18 deletions(-)
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index a26ffbe..dfb07dd 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -37,9 +37,12 @@ struct trace_eval_map {
tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data);
extern int
tracepoint_probe_register_prio(struct tracepoint *tp, void *probe, void *data,
- int prio);
+ int prio, bool dynamic);
+extern int dynamic_tracepoint_probe_register(struct tracepoint *tp,
+ void *probe, void *data);
extern int
-tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data);
+tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data,
+ bool dynamic);
extern void
for_each_kernel_tracepoint(void (*fct)(struct tracepoint *tp, void *priv),
void *priv);
@@ -210,13 +213,13 @@ static inline void tracepoint_synchronize_unregister(void)
int prio) \
{ \
return tracepoint_probe_register_prio(&__tracepoint_##name, \
- (void *)probe, data, prio); \
+ (void *)probe, data, prio, false); \
} \
static inline int \
unregister_trace_##name(void (*probe)(data_proto), void *data) \
{ \
return tracepoint_probe_unregister(&__tracepoint_##name,\
- (void *)probe, data); \
+ (void *)probe, data, false); \
} \
static inline void \
check_trace_callback_type_##name(void (*cb)(data_proto)) \
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 83dfd0d..1324393 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -297,7 +297,7 @@ int trace_event_reg(struct trace_event_call *call,
case TRACE_REG_UNREGISTER:
tracepoint_probe_unregister(call->tp,
call->class->probe,
- file);
+ file, false);
return 0;
#ifdef CONFIG_PERF_EVENTS
@@ -308,7 +308,7 @@ int trace_event_reg(struct trace_event_call *call,
case TRACE_REG_PERF_UNREGISTER:
tracepoint_probe_unregister(call->tp,
call->class->perf_probe,
- call);
+ call, false);
return 0;
case TRACE_REG_PERF_OPEN:
case TRACE_REG_PERF_CLOSE:
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c
index 685c50a..4068809 100644
--- a/kernel/tracepoint.c
+++ b/kernel/tracepoint.c
@@ -192,12 +192,15 @@ static void *func_remove(struct tracepoint_func **funcs,
* Add the probe function to a tracepoint.
*/
static int tracepoint_add_func(struct tracepoint *tp,
- struct tracepoint_func *func, int prio)
+ struct tracepoint_func *func, int prio,
+ bool dynamic)
{
struct tracepoint_func *old, *tp_funcs;
int ret;
- if (tp->regfunc && !static_key_enabled(&tp->key)) {
+ if (tp->regfunc &&
+ ((dynamic && !(atomic_read(&tp->key.enabled) > 0)) ||
+ !static_key_enabled(&tp->key))) {
ret = tp->regfunc();
if (ret < 0)
return ret;
@@ -219,7 +222,9 @@ static int tracepoint_add_func(struct tracepoint *tp,
* is used.
*/
rcu_assign_pointer(tp->funcs, tp_funcs);
- if (!static_key_enabled(&tp->key))
+ if (dynamic && !(atomic_read(&tp->key.enabled) > 0))
+ atomic_inc(&tp->key.enabled);
+ else if (!dynamic && !static_key_enabled(&tp->key))
static_key_slow_inc(&tp->key);
release_probes(old);
return 0;
@@ -232,7 +237,7 @@ static int tracepoint_add_func(struct tracepoint *tp,
* by preempt_disable around the call site.
*/
static int tracepoint_remove_func(struct tracepoint *tp,
- struct tracepoint_func *func)
+ struct tracepoint_func *func, bool dynamic)
{
struct tracepoint_func *old, *tp_funcs;
@@ -246,10 +251,14 @@ static int tracepoint_remove_func(struct tracepoint *tp,
if (!tp_funcs) {
/* Removed last function */
- if (tp->unregfunc && static_key_enabled(&tp->key))
+ if (tp->unregfunc &&
+ ((dynamic && (atomic_read(&tp->key.enabled) > 0)) ||
+ static_key_enabled(&tp->key)))
tp->unregfunc();
- if (static_key_enabled(&tp->key))
+ if (dynamic && (atomic_read(&tp->key.enabled) > 0))
+ atomic_dec(&tp->key.enabled);
+ else if (!dynamic && static_key_enabled(&tp->key))
static_key_slow_dec(&tp->key);
}
rcu_assign_pointer(tp->funcs, tp_funcs);
@@ -258,7 +267,7 @@ static int tracepoint_remove_func(struct tracepoint *tp,
}
/**
- * tracepoint_probe_register - Connect a probe to a tracepoint
+ * tracepoint_probe_register_prio - Connect a probe to a tracepoint
* @tp: tracepoint
* @probe: probe handler
* @data: tracepoint data
@@ -271,7 +280,7 @@ static int tracepoint_remove_func(struct tracepoint *tp,
* within module exit functions.
*/
int tracepoint_probe_register_prio(struct tracepoint *tp, void *probe,
- void *data, int prio)
+ void *data, int prio, bool dynamic)
{
struct tracepoint_func tp_func;
int ret;
@@ -280,7 +289,7 @@ int tracepoint_probe_register_prio(struct tracepoint *tp, void *probe,
tp_func.func = probe;
tp_func.data = data;
tp_func.prio = prio;
- ret = tracepoint_add_func(tp, &tp_func, prio);
+ ret = tracepoint_add_func(tp, &tp_func, prio, dynamic);
mutex_unlock(&tracepoints_mutex);
return ret;
}
@@ -301,10 +310,18 @@ int tracepoint_probe_register_prio(struct tracepoint *tp, void *probe,
*/
int tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data)
{
- return tracepoint_probe_register_prio(tp, probe, data, TRACEPOINT_DEFAULT_PRIO);
+ return tracepoint_probe_register_prio(tp, probe, data, TRACEPOINT_DEFAULT_PRIO, false);
}
EXPORT_SYMBOL_GPL(tracepoint_probe_register);
+int dynamic_tracepoint_probe_register(struct tracepoint *tp, void *probe,
+ void *data)
+{
+ return tracepoint_probe_register_prio(tp, probe, data,
+ TRACEPOINT_DEFAULT_PRIO, true);
+}
+EXPORT_SYMBOL_GPL(dynamic_tracepoint_probe_register);
+
/**
* tracepoint_probe_unregister - Disconnect a probe from a tracepoint
* @tp: tracepoint
@@ -313,7 +330,8 @@ int tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data)
*
* Returns 0 if ok, error value on error.
*/
-int tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data)
+int tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data,
+ bool dynamic)
{
struct tracepoint_func tp_func;
int ret;
@@ -321,7 +339,7 @@ int tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data)
mutex_lock(&tracepoints_mutex);
tp_func.func = probe;
tp_func.data = data;
- ret = tracepoint_remove_func(tp, &tp_func);
+ ret = tracepoint_remove_func(tp, &tp_func, dynamic);
mutex_unlock(&tracepoints_mutex);
return ret;
}
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 24/32] tracing: Add 'onmax' hist trigger action support |
| Message-ID | <tWL98-8u4-29@gated-at.bofh.it> |
| In reply to | #1675146 |
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: 2255 } hitcount: 239 \
common_timestamp-$ts0: 0
max: 27 next_comm: cyclictest \
prev_pid: 0 prev_prio: 120 prev_comm: swapper/1 \
{ next_pid: 2256 } hitcount: 2355 common_timestamp-$ts0: 0 \
max: 49 next_comm: cyclictest \
prev_pid: 0 prev_prio: 120 prev_comm: swapper/0
Totals:
Hits: 12970
Entries: 2
Dropped: 0
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace_events_hist.c | 310 ++++++++++++++++++++++++++++++++++-----
1 file changed, 276 insertions(+), 34 deletions(-)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index b1f859c..d191f1a 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -282,6 +282,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 {
@@ -318,6 +322,12 @@ struct action_data {
char *match_event_system;
char *synth_event_name;
struct synth_event *synth_event;
+
+ char *onmax_var_str;
+ char *onmax_fn_name;
+ unsigned int max_var_ref_idx;
+ struct hist_field *max_var;
+ struct hist_field *onmax_var;
};
static LIST_HEAD(synth_event_list);
@@ -1493,7 +1503,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;
@@ -1612,7 +1623,7 @@ static void hist_trigger_elt_data_free(struct tracing_map_elt *elt)
struct hist_elt_data *private_data = elt->private_data;
unsigned int i, n_str;
- n_str = hist_data->n_field_var_str;
+ n_str = hist_data->n_field_var_str + hist_data->n_max_var_str;
for (i = 0; i < n_str; i++)
kfree(private_data->field_var_str[i]);
@@ -1647,7 +1658,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;
for (i = 0; i < n_str; i++) {
elt_data->field_var_str[i] = kzalloc(size, GFP_KERNEL);
@@ -2504,6 +2515,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)
@@ -2613,6 +2633,222 @@ static struct field_var *create_field_var(struct hist_trigger_data *hist_data,
return create_field_var(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->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: %-50s", 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->max_var->var.idx;
+ unsigned int max_var_ref_idx = data->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->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_call *call = hist_data->event_file->event_call;
+ 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;
+ const char *event_name;
+ 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++;
+
+ event_name = trace_event_name(call);
+ 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;
+
+ ref_field->var.idx = var_field->var.idx;
+ ref_field->var.hist_data = hist_data;
+ ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL);
+ ref_field->type = kstrdup(var_field->type, GFP_KERNEL);
+ if (!ref_field->name || !ref_field->type) {
+ 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->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->max_var = max_var;
+
+ for (i = 0; i < data->n_params; i++) {
+ param = kstrdup(data->params[i], GFP_KERNEL);
+ if (!param)
+ 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);
+ }
+
+ hist_data->actions[hist_data->n_actions++] = data;
+ 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(¶ms, ",");
+ if (!param)
+ 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)
+ return ERR_PTR(-EINVAL);
+ data->onmax_var_str = kstrdup(onmax_var_str, GFP_KERNEL);
+
+ 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)
+ goto free;
+
+ ret = parse_action_params(params, data);
+ if (ret)
+ goto free;
+ }
+ data->onmax_fn_name = kstrdup(onmax_fn_name, GFP_KERNEL);
+
+ if (!data->onmax_var_str || !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;
@@ -2689,37 +2925,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(¶ms, ",");
- if (!param)
- 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)
@@ -3313,6 +3518,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);
}
@@ -3341,6 +3548,18 @@ static int create_actions(struct hist_trigger_data *hist_data,
onmatch_destroy(data);
return ret;
}
+ } else if (strncmp(str, "onmax(", strlen("onmax(")) == 0) {
+ char *action_str = str + strlen("onmax(");
+
+ data = onmax_parse(action_str);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ ret = onmax_create(hist_data, data);
+ if (ret) {
+ onmax_destroy(data);
+ return ret;
+ }
}
}
@@ -3355,9 +3574,30 @@ static void print_actions(struct seq_file *m,
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)
@@ -3388,6 +3628,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);
}
}
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 03/32] ring-buffer: Add interface for setting absolute time stamps |
| Message-ID | <tWL97-8u4-17@gated-at.bofh.it> |
| In reply to | #1675146 |
Define a new function, tracing_set_time_stamp_abs(), which can be used
to enable or disable the use of absolute timestamps rather than time
deltas for a trace array.
This resets the buffer to prevent a mix of time deltas and absolute
timestamps.
Only the interface is added here; a subsequent patch will add the
underlying implementation.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
include/linux/ring_buffer.h | 2 ++
kernel/trace/ring_buffer.c | 11 +++++++++++
kernel/trace/trace.c | 25 ++++++++++++++++++++++++-
kernel/trace/trace.h | 2 ++
4 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
index ee9b461..28e3472 100644
--- a/include/linux/ring_buffer.h
+++ b/include/linux/ring_buffer.h
@@ -180,6 +180,8 @@ void ring_buffer_normalize_time_stamp(struct ring_buffer *buffer,
int cpu, u64 *ts);
void ring_buffer_set_clock(struct ring_buffer *buffer,
u64 (*clock)(void));
+void ring_buffer_set_time_stamp_abs(struct ring_buffer *buffer, bool abs);
+bool ring_buffer_time_stamp_abs(struct ring_buffer *buffer);
size_t ring_buffer_page_len(void *page);
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 4ae268e..f544738 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -485,6 +485,7 @@ struct ring_buffer {
u64 (*clock)(void);
struct rb_irq_work irq_work;
+ bool time_stamp_abs;
};
struct ring_buffer_iter {
@@ -1379,6 +1380,16 @@ void ring_buffer_set_clock(struct ring_buffer *buffer,
buffer->clock = clock;
}
+void ring_buffer_set_time_stamp_abs(struct ring_buffer *buffer, bool abs)
+{
+ buffer->time_stamp_abs = abs;
+}
+
+bool ring_buffer_time_stamp_abs(struct ring_buffer *buffer)
+{
+ return buffer->time_stamp_abs;
+}
+
static void rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer);
static inline unsigned long rb_page_entries(struct buffer_page *bpage)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 19ac208..da8cd51 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -2169,7 +2169,7 @@ struct ring_buffer_event *
*current_rb = trace_file->tr->trace_buffer.buffer;
- if ((trace_file->flags &
+ if (!ring_buffer_time_stamp_abs(*current_rb) && (trace_file->flags &
(EVENT_FILE_FL_SOFT_DISABLED | EVENT_FILE_FL_FILTERED)) &&
(entry = this_cpu_read(trace_buffered_event))) {
/* Try to use the per cpu buffer first */
@@ -6079,6 +6079,29 @@ static int tracing_clock_open(struct inode *inode, struct file *file)
return ret;
}
+int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs)
+{
+ mutex_lock(&trace_types_lock);
+
+ ring_buffer_set_time_stamp_abs(tr->trace_buffer.buffer, abs);
+
+ /*
+ * New timestamps may not be consistent with the previous setting.
+ * Reset the buffer so that it doesn't have incomparable timestamps.
+ */
+ tracing_reset_online_cpus(&tr->trace_buffer);
+
+#ifdef CONFIG_TRACER_MAX_TRACE
+ if (tr->flags & TRACE_ARRAY_FL_GLOBAL && tr->max_buffer.buffer)
+ ring_buffer_set_time_stamp_abs(tr->max_buffer.buffer, abs);
+ tracing_reset_online_cpus(&tr->max_buffer);
+#endif
+
+ mutex_unlock(&trace_types_lock);
+
+ return 0;
+}
+
struct ftrace_buffer_info {
struct trace_iterator iter;
void *spare;
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 69a3ab3..155ad9a 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -281,6 +281,8 @@ enum {
extern int trace_array_get(struct trace_array *tr);
extern void trace_array_put(struct trace_array *tr);
+extern int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs);
+
/*
* The global tracer (top) should be the first trace array added,
* but we check the flag anyway.
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 13/32] tracing: Add per-element variable support to tracing_map |
| Message-ID | <tWL98-8u4-35@gated-at.bofh.it> |
| In reply to | #1675146 |
In order to allow information to be passed between trace events, add
support for per-element variables to tracing_map. This provides a
means for histograms to associate a value or values with an entry when
it's saved or updated, and retrieved by a subsequent event occurrences.
Variables can be set using tracing_map_set_var() and read using
tracing_map_read_var(). tracing_map_var_set() returns true or false
depending on whether or not the variable has been set or not, which is
important for event-matching applications.
tracing_map_read_var_once() reads the variable and resets it to the
'unset' state, implementing read-once variables, which are also
important for event-matching uses.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/tracing_map.c | 113 +++++++++++++++++++++++++++++++++++++++++++++
kernel/trace/tracing_map.h | 11 +++++
2 files changed, 124 insertions(+)
diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c
index 0a689bb..f987069 100644
--- a/kernel/trace/tracing_map.c
+++ b/kernel/trace/tracing_map.c
@@ -66,6 +66,73 @@ u64 tracing_map_read_sum(struct tracing_map_elt *elt, unsigned int i)
return (u64)atomic64_read(&elt->fields[i].sum);
}
+/**
+ * tracing_map_set_var - Assign a tracing_map_elt's variable field
+ * @elt: The tracing_map_elt
+ * @i: The index of the given variable associated with the tracing_map_elt
+ * @n: The value to assign
+ *
+ * Assign n to variable i associated with the specified tracing_map_elt
+ * instance. The index i is the index returned by the call to
+ * tracing_map_add_var() when the tracing map was set up.
+ */
+void tracing_map_set_var(struct tracing_map_elt *elt, unsigned int i, u64 n)
+{
+ atomic64_set(&elt->vars[i], n);
+ elt->var_set[i] = true;
+}
+
+/**
+ * tracing_map_var_set - Return whether or not a variable has been set
+ * @elt: The tracing_map_elt
+ * @i: The index of the given variable associated with the tracing_map_elt
+ *
+ * Return true if the variable has been set, false otherwise. The
+ * index i is the index returned by the call to tracing_map_add_var()
+ * when the tracing map was set up.
+ */
+bool tracing_map_var_set(struct tracing_map_elt *elt, unsigned int i)
+{
+ return elt->var_set[i];
+}
+
+/**
+ * tracing_map_read_var - Return the value of a tracing_map_elt's variable field
+ * @elt: The tracing_map_elt
+ * @i: The index of the given variable associated with the tracing_map_elt
+ *
+ * Retrieve the value of the variable i associated with the specified
+ * tracing_map_elt instance. The index i is the index returned by the
+ * call to tracing_map_add_var() when the tracing map was set
+ * up.
+ *
+ * Return: The variable value associated with field i for elt.
+ */
+u64 tracing_map_read_var(struct tracing_map_elt *elt, unsigned int i)
+{
+ return (u64)atomic64_read(&elt->vars[i]);
+}
+
+/**
+ * tracing_map_read_var_once - Return and reset a tracing_map_elt's variable field
+ * @elt: The tracing_map_elt
+ * @i: The index of the given variable associated with the tracing_map_elt
+ *
+ * Retrieve the value of the variable i associated with the specified
+ * tracing_map_elt instance, and reset the variable to the 'not set'
+ * state. The index i is the index returned by the call to
+ * tracing_map_add_var() when the tracing map was set up. The reset
+ * essentially makes the variable a read-once variable if it's only
+ * accessed using this function.
+ *
+ * Return: The variable value associated with field i for elt.
+ */
+u64 tracing_map_read_var_once(struct tracing_map_elt *elt, unsigned int i)
+{
+ elt->var_set[i] = false;
+ return (u64)atomic64_read(&elt->vars[i]);
+}
+
int tracing_map_cmp_string(void *val_a, void *val_b)
{
char *a = val_a;
@@ -171,6 +238,28 @@ int tracing_map_add_sum_field(struct tracing_map *map)
}
/**
+ * tracing_map_add_var - Add a field describing a tracing_map var
+ * @map: The tracing_map
+ *
+ * Add a var to the map and return the index identifying it in the map
+ * and associated tracing_map_elts. This is the index used for
+ * instance to update a var for a particular tracing_map_elt using
+ * tracing_map_update_var() or reading it via tracing_map_read_var().
+ *
+ * Return: The index identifying the var in the map and associated
+ * tracing_map_elts, or -EINVAL on error.
+ */
+int tracing_map_add_var(struct tracing_map *map)
+{
+ int ret = -EINVAL;
+
+ if (map->n_vars < TRACING_MAP_VARS_MAX)
+ ret = map->n_vars++;
+
+ return ret;
+}
+
+/**
* tracing_map_add_key_field - Add a field describing a tracing_map key
* @map: The tracing_map
* @offset: The offset within the key
@@ -277,6 +366,11 @@ static void tracing_map_elt_clear(struct tracing_map_elt *elt)
if (elt->fields[i].cmp_fn == tracing_map_cmp_atomic64)
atomic64_set(&elt->fields[i].sum, 0);
+ for (i = 0; i < elt->map->n_vars; i++) {
+ atomic64_set(&elt->vars[i], 0);
+ elt->var_set[i] = false;
+ }
+
if (elt->map->ops && elt->map->ops->elt_clear)
elt->map->ops->elt_clear(elt);
}
@@ -303,6 +397,8 @@ static void tracing_map_elt_free(struct tracing_map_elt *elt)
if (elt->map->ops && elt->map->ops->elt_free)
elt->map->ops->elt_free(elt);
kfree(elt->fields);
+ kfree(elt->vars);
+ kfree(elt->var_set);
kfree(elt->key);
kfree(elt);
}
@@ -330,6 +426,18 @@ static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map)
goto free;
}
+ elt->vars = kcalloc(map->n_vars, sizeof(*elt->vars), GFP_KERNEL);
+ if (!elt->vars) {
+ err = -ENOMEM;
+ goto free;
+ }
+
+ elt->var_set = kcalloc(map->n_vars, sizeof(*elt->var_set), GFP_KERNEL);
+ if (!elt->var_set) {
+ err = -ENOMEM;
+ goto free;
+ }
+
tracing_map_elt_init_fields(elt);
if (map->ops && map->ops->elt_alloc) {
@@ -833,6 +941,11 @@ static struct tracing_map_elt *copy_elt(struct tracing_map_elt *elt)
dup_elt->fields[i].cmp_fn = elt->fields[i].cmp_fn;
}
+ for (i = 0; i < elt->map->n_vars; i++) {
+ atomic64_set(&dup_elt->vars[i], atomic64_read(&elt->vars[i]));
+ dup_elt->var_set[i] = elt->var_set[i];
+ }
+
return dup_elt;
}
diff --git a/kernel/trace/tracing_map.h b/kernel/trace/tracing_map.h
index f097511..303bc4f 100644
--- a/kernel/trace/tracing_map.h
+++ b/kernel/trace/tracing_map.h
@@ -9,6 +9,7 @@
#define TRACING_MAP_VALS_MAX 3
#define TRACING_MAP_FIELDS_MAX (TRACING_MAP_KEYS_MAX + \
TRACING_MAP_VALS_MAX)
+#define TRACING_MAP_VARS_MAX 16
#define TRACING_MAP_SORT_KEYS_MAX 2
typedef int (*tracing_map_cmp_fn_t) (void *val_a, void *val_b);
@@ -136,6 +137,8 @@ struct tracing_map_field {
struct tracing_map_elt {
struct tracing_map *map;
struct tracing_map_field *fields;
+ atomic64_t *vars;
+ bool *var_set;
void *key;
void *private_data;
};
@@ -191,6 +194,7 @@ struct tracing_map {
int key_idx[TRACING_MAP_KEYS_MAX];
unsigned int n_keys;
struct tracing_map_sort_key sort_key;
+ unsigned int n_vars;
atomic64_t hits;
atomic64_t drops;
};
@@ -247,6 +251,7 @@ struct tracing_map_ops {
extern int tracing_map_init(struct tracing_map *map);
extern int tracing_map_add_sum_field(struct tracing_map *map);
+extern int tracing_map_add_var(struct tracing_map *map);
extern int tracing_map_add_key_field(struct tracing_map *map,
unsigned int offset,
tracing_map_cmp_fn_t cmp_fn);
@@ -266,7 +271,13 @@ extern tracing_map_cmp_fn_t tracing_map_cmp_num(int field_size,
extern void tracing_map_update_sum(struct tracing_map_elt *elt,
unsigned int i, u64 n);
+extern void tracing_map_set_var(struct tracing_map_elt *elt,
+ unsigned int i, u64 n);
+extern bool tracing_map_var_set(struct tracing_map_elt *elt, unsigned int i);
extern u64 tracing_map_read_sum(struct tracing_map_elt *elt, unsigned int i);
+extern u64 tracing_map_read_var(struct tracing_map_elt *elt, unsigned int i);
+extern u64 tracing_map_read_var_once(struct tracing_map_elt *elt, unsigned int i);
+
extern void tracing_map_set_field_descr(struct tracing_map *map,
unsigned int i,
unsigned int key_offset,
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 09/32] tracing: Make traceprobe parsing code reusable |
| Message-ID | <tWL97-8u4-19@gated-at.bofh.it> |
| In reply to | #1675146 |
traceprobe_probes_write() and traceprobe_command() actually contain
nothing that ties them to kprobes - the code is generically useful for
similar types of parsing elsewhere, so separate it out and move it to
trace.c/trace.h.
Other than moving it, the only change is in naming:
traceprobe_probes_write() becomes trace_parse_run_command() and
traceprobe_command() becomes trace_run_command().
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.c | 86 +++++++++++++++++++++++++++++++++++++++++++++
kernel/trace/trace.h | 7 ++++
kernel/trace/trace_kprobe.c | 18 +++++-----
kernel/trace/trace_probe.c | 86 ---------------------------------------------
kernel/trace/trace_probe.h | 7 ----
kernel/trace/trace_uprobe.c | 2 +-
6 files changed, 103 insertions(+), 103 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index da8cd51..f66a3df 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -8082,6 +8082,92 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
}
EXPORT_SYMBOL_GPL(ftrace_dump);
+int trace_run_command(const char *buf, int (*createfn)(int, char **))
+{
+ char **argv;
+ int argc, ret;
+
+ argc = 0;
+ ret = 0;
+ argv = argv_split(GFP_KERNEL, buf, &argc);
+ if (!argv)
+ return -ENOMEM;
+
+ if (argc)
+ ret = createfn(argc, argv);
+
+ argv_free(argv);
+
+ return ret;
+}
+
+#define WRITE_BUFSIZE 4096
+
+ssize_t trace_parse_run_command(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos,
+ int (*createfn)(int, char **))
+{
+ char *kbuf, *buf, *tmp;
+ int ret = 0;
+ size_t done = 0;
+ size_t size;
+
+ kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ while (done < count) {
+ size = count - done;
+
+ if (size >= WRITE_BUFSIZE)
+ size = WRITE_BUFSIZE - 1;
+
+ if (copy_from_user(kbuf, buffer + done, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ kbuf[size] = '\0';
+ buf = kbuf;
+ do {
+ tmp = strchr(buf, '\n');
+ if (tmp) {
+ *tmp = '\0';
+ size = tmp - buf + 1;
+ } else {
+ size = strlen(buf);
+ if (done + size < count) {
+ if (buf != kbuf)
+ break;
+ /* This can accept WRITE_BUFSIZE - 2 ('\n' + '\0') */
+ pr_warn("Line length is too long: Should be less than %d\n",
+ WRITE_BUFSIZE - 2);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ done += size;
+
+ /* Remove comments */
+ tmp = strchr(buf, '#');
+
+ if (tmp)
+ *tmp = '\0';
+
+ ret = trace_run_command(buf, createfn);
+ if (ret)
+ goto out;
+ buf += size;
+
+ } while (done < count);
+ }
+ ret = done;
+
+out:
+ kfree(kbuf);
+
+ return ret;
+}
+
__init static int tracer_alloc_buffers(void)
{
int ring_buf_size;
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index fc892b9..78c7694 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1730,6 +1730,13 @@ extern int trace_event_enable_disable(struct trace_event_file *file,
int trace_keep_overwrite(struct tracer *tracer, u32 mask, int set);
int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled);
+#define MAX_EVENT_NAME_LEN 64
+
+extern int trace_run_command(const char *buf, int (*createfn)(int, char**));
+extern ssize_t trace_parse_run_command(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos,
+ int (*createfn)(int, char**));
+
/*
* Normal trace_printk() and friends allocates special buffers
* to do the manipulation, as well as saves the print formats
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index c129fca..577cc69 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -902,8 +902,8 @@ static int probes_open(struct inode *inode, struct file *file)
static ssize_t probes_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
- return traceprobe_probes_write(file, buffer, count, ppos,
- create_trace_kprobe);
+ return trace_parse_run_command(file, buffer, count, ppos,
+ create_trace_kprobe);
}
static const struct file_operations kprobe_events_ops = {
@@ -1428,9 +1428,9 @@ static __init int kprobe_trace_self_tests_init(void)
pr_info("Testing kprobe tracing: ");
- ret = traceprobe_command("p:testprobe kprobe_trace_selftest_target "
- "$stack $stack0 +0($stack)",
- create_trace_kprobe);
+ ret = trace_run_command("p:testprobe kprobe_trace_selftest_target "
+ "$stack $stack0 +0($stack)",
+ create_trace_kprobe);
if (WARN_ON_ONCE(ret)) {
pr_warn("error on probing function entry.\n");
warn++;
@@ -1450,8 +1450,8 @@ static __init int kprobe_trace_self_tests_init(void)
}
}
- ret = traceprobe_command("r:testprobe2 kprobe_trace_selftest_target "
- "$retval", create_trace_kprobe);
+ ret = trace_run_command("r:testprobe2 kprobe_trace_selftest_target "
+ "$retval", create_trace_kprobe);
if (WARN_ON_ONCE(ret)) {
pr_warn("error on probing function return.\n");
warn++;
@@ -1521,13 +1521,13 @@ static __init int kprobe_trace_self_tests_init(void)
disable_trace_kprobe(tk, file);
}
- ret = traceprobe_command("-:testprobe", create_trace_kprobe);
+ ret = trace_run_command("-:testprobe", create_trace_kprobe);
if (WARN_ON_ONCE(ret)) {
pr_warn("error on deleting a probe.\n");
warn++;
}
- ret = traceprobe_command("-:testprobe2", create_trace_kprobe);
+ ret = trace_run_command("-:testprobe2", create_trace_kprobe);
if (WARN_ON_ONCE(ret)) {
pr_warn("error on deleting a probe.\n");
warn++;
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 52478f0..d593573 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -623,92 +623,6 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
kfree(arg->comm);
}
-int traceprobe_command(const char *buf, int (*createfn)(int, char **))
-{
- char **argv;
- int argc, ret;
-
- argc = 0;
- ret = 0;
- argv = argv_split(GFP_KERNEL, buf, &argc);
- if (!argv)
- return -ENOMEM;
-
- if (argc)
- ret = createfn(argc, argv);
-
- argv_free(argv);
-
- return ret;
-}
-
-#define WRITE_BUFSIZE 4096
-
-ssize_t traceprobe_probes_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *ppos,
- int (*createfn)(int, char **))
-{
- char *kbuf, *buf, *tmp;
- int ret = 0;
- size_t done = 0;
- size_t size;
-
- kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
-
- while (done < count) {
- size = count - done;
-
- if (size >= WRITE_BUFSIZE)
- size = WRITE_BUFSIZE - 1;
-
- if (copy_from_user(kbuf, buffer + done, size)) {
- ret = -EFAULT;
- goto out;
- }
- kbuf[size] = '\0';
- buf = kbuf;
- do {
- tmp = strchr(buf, '\n');
- if (tmp) {
- *tmp = '\0';
- size = tmp - buf + 1;
- } else {
- size = strlen(buf);
- if (done + size < count) {
- if (buf != kbuf)
- break;
- /* This can accept WRITE_BUFSIZE - 2 ('\n' + '\0') */
- pr_warn("Line length is too long: Should be less than %d\n",
- WRITE_BUFSIZE - 2);
- ret = -EINVAL;
- goto out;
- }
- }
- done += size;
-
- /* Remove comments */
- tmp = strchr(buf, '#');
-
- if (tmp)
- *tmp = '\0';
-
- ret = traceprobe_command(buf, createfn);
- if (ret)
- goto out;
- buf += size;
-
- } while (done < count);
- }
- ret = done;
-
-out:
- kfree(kbuf);
-
- return ret;
-}
-
static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
bool is_return)
{
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 903273c..fb66e3e 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -42,7 +42,6 @@
#define MAX_TRACE_ARGS 128
#define MAX_ARGSTR_LEN 63
-#define MAX_EVENT_NAME_LEN 64
#define MAX_STRING_SIZE PATH_MAX
/* Reserved field names */
@@ -356,12 +355,6 @@ extern int traceprobe_conflict_field_name(const char *name,
extern int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset);
-extern ssize_t traceprobe_probes_write(struct file *file,
- const char __user *buffer, size_t count, loff_t *ppos,
- int (*createfn)(int, char**));
-
-extern int traceprobe_command(const char *buf, int (*createfn)(int, char**));
-
/* Sum up total data length for dynamic arraies (strings) */
static nokprobe_inline int
__get_data_size(struct trace_probe *tp, struct pt_regs *regs)
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index a7581fe..402120b 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -651,7 +651,7 @@ static int probes_open(struct inode *inode, struct file *file)
static ssize_t probes_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
- return traceprobe_probes_write(file, buffer, count, ppos, create_trace_uprobe);
+ return trace_parse_run_command(file, buffer, count, ppos, create_trace_uprobe);
}
static const struct file_operations uprobe_events_ops = {
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 28/32] tracing: Add hist trigger support for variable reference aliases |
| Message-ID | <tWL98-8u4-39@gated-at.bofh.it> |
| In reply to | #1675146 |
Add support for alias=$somevar where alias can be used as
onmatch($alias).
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace_events_hist.c | 46 +++++++++++++++++++++++++++++++++++++---
1 file changed, 43 insertions(+), 3 deletions(-)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 904a635..799b95e 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -225,6 +225,7 @@ enum hist_field_flags {
HIST_FIELD_FL_EXPR = 16384,
HIST_FIELD_FL_VAR_REF = 32768,
HIST_FIELD_FL_CPU = 65536,
+ HIST_FIELD_FL_ALIAS = 131072,
};
struct hist_trigger_attrs {
@@ -1414,7 +1415,8 @@ static const char *hist_field_name(struct hist_field *field,
if (field->field)
field_name = field->field->name;
- else if (field->flags & HIST_FIELD_FL_LOG2)
+ else if (field->flags & HIST_FIELD_FL_LOG2 ||
+ field->flags & HIST_FIELD_FL_ALIAS)
field_name = hist_field_name(field->operands[0], ++level);
else if (field->flags & HIST_FIELD_FL_TIMESTAMP)
field_name = "$common_timestamp";
@@ -1819,7 +1821,7 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
hist_field->hist_data = hist_data;
- if (flags & HIST_FIELD_FL_EXPR)
+ if (flags & HIST_FIELD_FL_EXPR || flags & HIST_FIELD_FL_ALIAS)
goto out; /* caller will populate */
if (flags & HIST_FIELD_FL_VAR_REF) {
@@ -2013,6 +2015,34 @@ static struct hist_field *parse_var_ref(char *system, char *event_name,
return field;
}
+static struct hist_field *create_alias(struct hist_trigger_data *hist_data,
+ struct hist_field *var_ref,
+ char *var_name)
+{
+ struct hist_field *alias = NULL;
+ unsigned long flags = HIST_FIELD_FL_ALIAS | HIST_FIELD_FL_VAR |
+ HIST_FIELD_FL_VAR_ONLY;
+
+ alias = create_hist_field(hist_data, NULL, flags, var_name);
+ if (!alias)
+ return NULL;
+
+ alias->fn = var_ref->fn;
+ alias->operands[0] = var_ref;
+ alias->var.idx = var_ref->var.idx;
+ alias->var.hist_data = var_ref->hist_data;
+ alias->size = var_ref->size;
+ alias->is_signed = var_ref->is_signed;
+ alias->type = kstrdup(var_ref->type, GFP_KERNEL);
+ if (!alias->type) {
+ kfree(alias->type);
+ destroy_hist_field(alias, 0);
+ return NULL;
+ }
+
+ return alias;
+}
+
struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
struct trace_event_file *file, char *str,
unsigned long *flags, char *var_name)
@@ -2036,6 +2066,13 @@ struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
if (hist_field) {
hist_data->var_refs[hist_data->n_var_refs] = hist_field;
hist_field->var_ref_idx = hist_data->n_var_refs++;
+ if (var_name) {
+ hist_field = create_alias(hist_data, hist_field, var_name);
+ if (!hist_field) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
return hist_field;
}
@@ -4152,8 +4189,11 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
seq_puts(m, "$common_timestamp");
else if (hist_field->flags & HIST_FIELD_FL_CPU)
seq_puts(m, "cpu");
- else if (field_name)
+ else if (field_name) {
+ if (hist_field->flags & HIST_FIELD_FL_ALIAS)
+ seq_putc(m, '$');
seq_printf(m, "%s", field_name);
+ }
if (hist_field->flags) {
const char *flags_str = get_hist_field_flags(hist_field);
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 32/32] tracing: Add a clock attribute for hist triggers |
| Message-ID | <tWL99-8u4-57@gated-at.bofh.it> |
| In reply to | #1675146 |
The default clock if timestamps are used in a histogram is "global".
If timestamps aren't used, the clock is irrelevant.
Use the "clock=" param only if you want to override the default
"global" clock for a histogram with timestamps.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
Documentation/trace/events.txt | 9 +++++++++
kernel/trace/trace_events_hist.c | 34 +++++++++++++++++++++++++++++++---
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/Documentation/trace/events.txt b/Documentation/trace/events.txt
index 888907c..20e4fd4 100644
--- a/Documentation/trace/events.txt
+++ b/Documentation/trace/events.txt
@@ -2173,6 +2173,15 @@ features have been added to the hist trigger support:
default it is in units of nanoseconds; appending '.usecs' to a
common_timestamp field changes the units to microseconds.
+A note on inter-event timestamps: If $common_timestamp is used in a
+histogram, the trace buffer is automatically switched over to using
+absolute timestamps and the "global" trace clock, in order to avoid
+bogus timestamp differences with other clocks that aren't coherent
+across CPUs. This can be overriden by specifying one of the other
+trace clocks instead, using the "clock=XXX" hist trigger attribute,
+where XXX is any of the clocks listed in the tracing/trace_clock
+pseudo-file.
+
These features are decribed in more detail in the following sections.
6.3.1 Histogram Variables
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 06049f8..af508a3 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -233,6 +233,7 @@ struct hist_trigger_attrs {
char *vals_str;
char *sort_key_str;
char *name;
+ char *clock;
bool pause;
bool cont;
bool clear;
@@ -1586,6 +1587,7 @@ static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
kfree(attrs->sort_key_str);
kfree(attrs->keys_str);
kfree(attrs->vals_str);
+ kfree(attrs->clock);
kfree(attrs);
}
@@ -1625,7 +1627,16 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
else if (strncmp(str, "name=", strlen("name=")) == 0)
attrs->name = kstrdup(str, GFP_KERNEL);
- else if (strncmp(str, "size=", strlen("size=")) == 0) {
+ else if (strncmp(str, "clock=", strlen("clock=")) == 0) {
+ strsep(&str, "=");
+ if (!str) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ str = strstrip(str);
+ attrs->clock = kstrdup(str, GFP_KERNEL);
+ } else if (strncmp(str, "size=", strlen("size=")) == 0) {
int map_bits = parse_map_size(str);
if (map_bits < 0) {
@@ -1688,6 +1699,12 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
goto free;
}
+ if (!attrs->clock) {
+ attrs->clock = kstrdup("global", GFP_KERNEL);
+ if (!attrs->clock)
+ goto free;
+ }
+
return attrs;
free:
destroy_hist_trigger_attrs(attrs);
@@ -4437,6 +4454,8 @@ static int event_hist_trigger_print(struct seq_file *m,
seq_puts(m, ".descending");
}
seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits));
+ if (hist_data->enable_timestamps)
+ seq_printf(m, ":clock=%s", hist_data->attrs->clock);
print_actions_spec(m, hist_data);
@@ -4702,10 +4721,19 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
goto out;
}
- ret++;
+ if (hist_data->enable_timestamps) {
+ char *clock = hist_data->attrs->clock;
+
+ ret = tracing_set_clock(file->tr, hist_data->attrs->clock);
+ if (ret) {
+ hist_err("Couldn't set trace_clock: ", clock);
+ goto out;
+ }
- if (hist_data->enable_timestamps)
tracing_set_time_stamp_abs(file->tr, true);
+ }
+
+ ret++;
out:
return ret;
}
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 25/32] tracing: Allow whitespace to surround hist trigger filter |
| Message-ID | <tWL99-8u4-63@gated-at.bofh.it> |
| In reply to | #1675146 |
The existing code only allows for one space before and after the 'if'
specifying the filter for a hist trigger. Add code to make that more
permissive as far as whitespace goes.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace_events_hist.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index d191f1a..53a0634 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -4632,7 +4632,7 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
struct event_trigger_ops *trigger_ops;
struct hist_trigger_data *hist_data;
bool remove = false;
- char *trigger;
+ char *trigger, *p;
int ret = 0;
if (!param)
@@ -4642,9 +4642,19 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
remove = true;
/* separate the trigger from the filter (k:v [if filter]) */
- trigger = strsep(¶m, " \t");
- if (!trigger)
- return -EINVAL;
+ trigger = param;
+ p = strstr(param, " if");
+ if (!p)
+ p = strstr(param, "\tif");
+ if (p) {
+ if (p == trigger)
+ return -EINVAL;
+ param = p + 1;
+ param = strstrip(param);
+ *p = '\0';
+ trigger = strstrip(trigger);
+ } else
+ param = NULL;
attrs = parse_hist_trigger_attrs(trigger);
if (IS_ERR(attrs))
@@ -4694,6 +4704,7 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
}
ret = cmd_ops->reg(glob, trigger_ops, trigger_data, file);
+
/*
* The above returns on success the # of triggers registered,
* but if it didn't register any it returns zero. Consider no
--
1.9.3
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2017-06-27 01:00 +0200 |
| Subject | [PATCH 05/32] tracing: Give event triggers access to ring_buffer_event |
| Message-ID | <tWL99-8u4-55@gated-at.bofh.it> |
| In reply to | #1675146 |
The ring_buffer event can provide a timestamp that may be useful to
various triggers - pass it into the handlers for that purpose.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
include/linux/trace_events.h | 14 ++++++-----
kernel/trace/trace.h | 9 +++----
kernel/trace/trace_events_hist.c | 11 +++++----
kernel/trace/trace_events_trigger.c | 47 +++++++++++++++++++++++--------------
4 files changed, 49 insertions(+), 32 deletions(-)
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index a556805..f333114 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -391,11 +391,13 @@ enum event_trigger_type {
extern int filter_match_preds(struct event_filter *filter, void *rec);
-extern enum event_trigger_type event_triggers_call(struct trace_event_file *file,
- void *rec);
-extern void event_triggers_post_call(struct trace_event_file *file,
- enum event_trigger_type tt,
- void *rec);
+extern enum event_trigger_type
+event_triggers_call(struct trace_event_file *file, void *rec,
+ struct ring_buffer_event *event);
+extern void
+event_triggers_post_call(struct trace_event_file *file,
+ enum event_trigger_type tt,
+ void *rec, struct ring_buffer_event *event);
bool trace_event_ignore_this_pid(struct trace_event_file *trace_file);
@@ -415,7 +417,7 @@ extern void event_triggers_post_call(struct trace_event_file *file,
if (!(eflags & EVENT_FILE_FL_TRIGGER_COND)) {
if (eflags & EVENT_FILE_FL_TRIGGER_MODE)
- event_triggers_call(file, NULL);
+ event_triggers_call(file, NULL, NULL);
if (eflags & EVENT_FILE_FL_SOFT_DISABLED)
return true;
if (eflags & EVENT_FILE_FL_PID_FILTER)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 155ad9a..fc892b9 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1269,7 +1269,7 @@ static inline void trace_buffer_unlock_commit(struct trace_array *tr,
unsigned long eflags = file->flags;
if (eflags & EVENT_FILE_FL_TRIGGER_COND)
- *tt = event_triggers_call(file, entry);
+ *tt = event_triggers_call(file, entry, event);
if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags) ||
(unlikely(file->flags & EVENT_FILE_FL_FILTERED) &&
@@ -1306,7 +1306,7 @@ static inline void trace_buffer_unlock_commit(struct trace_array *tr,
trace_buffer_unlock_commit(file->tr, buffer, event, irq_flags, pc);
if (tt)
- event_triggers_post_call(file, tt, entry);
+ event_triggers_post_call(file, tt, entry, event);
}
/**
@@ -1339,7 +1339,7 @@ static inline void trace_buffer_unlock_commit(struct trace_array *tr,
irq_flags, pc, regs);
if (tt)
- event_triggers_post_call(file, tt, entry);
+ event_triggers_post_call(file, tt, entry, event);
}
#define FILTER_PRED_INVALID ((unsigned short)-1)
@@ -1562,7 +1562,8 @@ extern void set_named_trigger_data(struct event_trigger_data *data,
*/
struct event_trigger_ops {
void (*func)(struct event_trigger_data *data,
- void *rec);
+ void *rec,
+ struct ring_buffer_event *rbe);
int (*init)(struct event_trigger_ops *ops,
struct event_trigger_data *data);
void (*free)(struct event_trigger_ops *ops,
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 7b55956..655c412 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -921,7 +921,8 @@ static inline void add_to_key(char *compound_key, void *key,
memcpy(compound_key + key_field->offset, key, size);
}
-static void event_hist_trigger(struct event_trigger_data *data, void *rec)
+static void event_hist_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
struct hist_trigger_data *hist_data = data->private_data;
bool use_compound_key = (hist_data->n_keys > 1);
@@ -1672,7 +1673,8 @@ __init int register_trigger_hist_cmd(void)
}
static void
-hist_enable_trigger(struct event_trigger_data *data, void *rec)
+hist_enable_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
struct enable_trigger_data *enable_data = data->private_data;
struct event_trigger_data *test;
@@ -1688,7 +1690,8 @@ __init int register_trigger_hist_cmd(void)
}
static void
-hist_enable_count_trigger(struct event_trigger_data *data, void *rec)
+hist_enable_count_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
if (!data->count)
return;
@@ -1696,7 +1699,7 @@ __init int register_trigger_hist_cmd(void)
if (data->count != -1)
(data->count)--;
- hist_enable_trigger(data, rec);
+ hist_enable_trigger(data, rec, event);
}
static struct event_trigger_ops hist_enable_trigger_ops = {
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index f2ac9d4..9b0fe31 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -63,7 +63,8 @@ void trigger_data_free(struct event_trigger_data *data)
* any trigger that should be deferred, ETT_NONE if nothing to defer.
*/
enum event_trigger_type
-event_triggers_call(struct trace_event_file *file, void *rec)
+event_triggers_call(struct trace_event_file *file, void *rec,
+ struct ring_buffer_event *event)
{
struct event_trigger_data *data;
enum event_trigger_type tt = ETT_NONE;
@@ -76,7 +77,7 @@ enum event_trigger_type
if (data->paused)
continue;
if (!rec) {
- data->ops->func(data, rec);
+ data->ops->func(data, rec, event);
continue;
}
filter = rcu_dereference_sched(data->filter);
@@ -86,7 +87,7 @@ enum event_trigger_type
tt |= data->cmd_ops->trigger_type;
continue;
}
- data->ops->func(data, rec);
+ data->ops->func(data, rec, event);
}
return tt;
}
@@ -108,7 +109,7 @@ enum event_trigger_type
void
event_triggers_post_call(struct trace_event_file *file,
enum event_trigger_type tt,
- void *rec)
+ void *rec, struct ring_buffer_event *event)
{
struct event_trigger_data *data;
@@ -116,7 +117,7 @@ enum event_trigger_type
if (data->paused)
continue;
if (data->cmd_ops->trigger_type & tt)
- data->ops->func(data, rec);
+ data->ops->func(data, rec, event);
}
}
EXPORT_SYMBOL_GPL(event_triggers_post_call);
@@ -909,7 +910,8 @@ void set_named_trigger_data(struct event_trigger_data *data,
}
static void
-traceon_trigger(struct event_trigger_data *data, void *rec)
+traceon_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
if (tracing_is_on())
return;
@@ -918,7 +920,8 @@ void set_named_trigger_data(struct event_trigger_data *data,
}
static void
-traceon_count_trigger(struct event_trigger_data *data, void *rec)
+traceon_count_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
if (tracing_is_on())
return;
@@ -933,7 +936,8 @@ void set_named_trigger_data(struct event_trigger_data *data,
}
static void
-traceoff_trigger(struct event_trigger_data *data, void *rec)
+traceoff_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
if (!tracing_is_on())
return;
@@ -942,7 +946,8 @@ void set_named_trigger_data(struct event_trigger_data *data,
}
static void
-traceoff_count_trigger(struct event_trigger_data *data, void *rec)
+traceoff_count_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
if (!tracing_is_on())
return;
@@ -1039,13 +1044,15 @@ void set_named_trigger_data(struct event_trigger_data *data,
#ifdef CONFIG_TRACER_SNAPSHOT
static void
-snapshot_trigger(struct event_trigger_data *data, void *rec)
+snapshot_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
tracing_snapshot();
}
static void
-snapshot_count_trigger(struct event_trigger_data *data, void *rec)
+snapshot_count_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
if (!data->count)
return;
@@ -1053,7 +1060,7 @@ void set_named_trigger_data(struct event_trigger_data *data,
if (data->count != -1)
(data->count)--;
- snapshot_trigger(data, rec);
+ snapshot_trigger(data, rec, event);
}
static int
@@ -1132,13 +1139,15 @@ static __init int register_trigger_snapshot_cmd(void)
#define STACK_SKIP 3
static void
-stacktrace_trigger(struct event_trigger_data *data, void *rec)
+stacktrace_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
trace_dump_stack(STACK_SKIP);
}
static void
-stacktrace_count_trigger(struct event_trigger_data *data, void *rec)
+stacktrace_count_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
if (!data->count)
return;
@@ -1146,7 +1155,7 @@ static __init int register_trigger_snapshot_cmd(void)
if (data->count != -1)
(data->count)--;
- stacktrace_trigger(data, rec);
+ stacktrace_trigger(data, rec, event);
}
static int
@@ -1208,7 +1217,8 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)
}
static void
-event_enable_trigger(struct event_trigger_data *data, void *rec)
+event_enable_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
struct enable_trigger_data *enable_data = data->private_data;
@@ -1219,7 +1229,8 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)
}
static void
-event_enable_count_trigger(struct event_trigger_data *data, void *rec)
+event_enable_count_trigger(struct event_trigger_data *data, void *rec,
+ struct ring_buffer_event *event)
{
struct enable_trigger_data *enable_data = data->private_data;
@@ -1233,7 +1244,7 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)
if (data->count != -1)
(data->count)--;
- event_enable_trigger(data, rec);
+ event_enable_trigger(data, rec, event);
}
int event_enable_trigger_print(struct seq_file *m,
--
1.9.3
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web