Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1275802 > unrolled thread
| Started by | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| First post | 2015-11-23 21:00 +0100 |
| Last post | 2015-11-29 16:30 +0100 |
| Articles | 16 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH 00/30] tracing: 'hist' triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:00 +0100
[PATCH v12 02/30] tracing: Make ftrace_event_field checking functions available Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:00 +0100
[PATCH v12 26/30] tracing: Add support for named triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:00 +0100
[PATCH v12 04/30] tracing: Add event record param to trigger_ops.func() Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:00 +0100
[PATCH v12 23/30] tracing: Add enable_hist/disable_hist triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:00 +0100
[PATCH v12 07/30] tracing: Add needs_rec flag to event triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:00 +0100
[PATCH v12 11/30] tracing: Add hist trigger support for multiple values ('vals=' param) Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:10 +0100
[PATCH v12 08/30] tracing: Add an unreg_all() callback to trigger commands Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:10 +0100
[PATCH v12 06/30] tracing: Add a per-event-trigger 'paused' field Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:10 +0100
[PATCH v12 03/30] tracing: Make event trigger functions available Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:10 +0100
[PATCH v12 01/30] tracing: Update cond flag when enabling or disabling a trigger Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:10 +0100
Re: [PATCH v12 01/30] tracing: Update cond flag when enabling or disabling a trigger Steven Rostedt <rostedt@goodmis.org> - 2015-11-23 22:30 +0100
Re: [PATCH v12 01/30] tracing: Update cond flag when enabling or disabling a trigger Namhyung Kim <namhyung@kernel.org> - 2015-11-25 02:30 +0100
[PATCH v12 10/30] tracing: Add 'hist' event trigger command Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-11-23 21:10 +0100
Re: [PATCH v12 10/30] tracing: Add 'hist' event trigger command Namhyung Kim <namhyung@kernel.org> - 2015-11-29 16:00 +0100
Re: [PATCH 00/30] tracing: 'hist' triggers Namhyung Kim <namhyung@kernel.org> - 2015-11-29 16:30 +0100
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:00 +0100 |
| Subject | [PATCH 00/30] tracing: 'hist' triggers |
| Message-ID | <qy5rk-6jA-5@gated-at.bofh.it> |
This is v12 of the 'hist triggers' patchset, incorporating suggestions
and fixing problems pointed out by Namhyung.
Changes from v11:
- Added tracing_map_lookup().
- Changed tracing_map_insert() to incorporate the hits/drops counter
updating. The changes allow clients to continue calling
tracing_map_insert() and update existing entries if desired, or
stop on the first drop, since the return value is still NULL in
that case.
- Changed the hist trigger code to continue calling
tracing_map_insert() rather than stopping on the first drop. The
idea is that there's no harm in continuing after the first drop -
if the user can't tolerate any drops, they'll still see a non-zero
drop count and can retry with a larger table, but if the user
doesn't care, the hit and drop counts are an accurate reflection
of how many events were tallied vs dropped.
- Added a new unreg_all() callback to event_command, and code to
call it when the trigger file is opened for truncate. Implemented
unreg_all() for hist triggers, which allows them to honor the
truncate flag while leaving existing triggers intact. This means
that '>>' should now be used for adding multiple hist triggers as
well as to clear/pause/cont an existing hist trigger.
- Included Namhyung's log2 patch.
- Fixed one of Masami's test cases which now requires using '>>' due
to the truncate changes.
- 'cont' and 'clear' no longer create a trigger if one doesn't exist.
- Rebased to latest trace/for-next.
- Various other smaller fixes and Documentation updates as noted by
Namhyung.
Changes from v10:
- Rebased to latest trace/for-next.
- Added Masami's ftrace/hist triggers kselftest patches.
- Added Masami's Tested-by: to the series.
Changes from v9:
v10 adds some major new features - 'named' hist triggers and support
for multiple hist triggers on a single event - basically all of the
features and suggestions suggested by Masami, except for one: I
decided after all not to implement the 'pause/continue/clear' commands
on the hist file. I've gotten so used to the habit of calling up the
previous trigger-setting command in the command history and simply
appending a 'pause/cont/clear' command to it (and later removing it by
prepending a '!') that making the user change the command doesn't seem
as naturally usable. Masami, if you disagree, let me know and I'll
reconsider it but at this point I'd rather not make that change.
In addition, I think I've fixed all the other various smaller problems
mentioned in the previous review, please let me know if I missed any...
- Added support for 'named' hist triggers
- Added support for multiple hist triggers on a single trace event
- Added documentation and examples for the above new features
- Streamlined the README to only include the essentials for hist triggers
- Fixed the 'hex' modifier for values, which was previously ignored
- Replaced the kmalloc of large arrays with a simple page-based scheme
- Fixed the tracing_map_insert() case where jhash returns 0
- Fixed various other small problems noted along the way
Changes from v8:
Same as v8, but with the RFC patch [ftrace: Add function_hist tracer]
removed, and rebased to latest trace/for-next.
Changes from v7:
This version refactors the commits as suggested by Masami. There are
now more commits, but the result should be much more reviewable. The
ending code is the same as before, modulo a couple minor bug fixes I
discovered while refactoring and testing.
I've also reviewed and fixed a number of shortcomings and errors in
the comments, and have added a new discussion of the tracing_map data
structures after Steve mentioned he found them confusing and/or
insufficiently documented.
Also, I kept Namhyung's string patch [tracing: Support string type key
properly] as submitted, but added a follow-on patch that refactors it
and fixes a problem I found with it that enabled static string keys to
contain random chars and therefore incorrect map insertions.
Changes from v6:
This version adds a new 'sym-offset' modifier as requested by Masami.
I implemented it as a modifier rather than using the trace option as
suggested, in part because I wanted to keep it all self-contained and
it seemed more consistent to just add it alongside the 'sym' modifier.
Also, hist triggers arent't really a tracer and therefore don't
directly tie into the option update/callback mechanism so making use
of it isn't as simple as a normal tracer.
I also changed the sort key specification to be stricter and signal an
error if the specified sort key wasn't found (rather than defaulting
to hitcount in those cases), also suggested by Masami. Thanks,
Masami, for your input!
Also updated the Documentation and tracing/README to reflect the
changes.
Changes from v5:
This version adds support for compound keys, along with the related
ability to sort using primary and secondary keys. This was mentioned
in previous versions as the last important piece that remained
unimplemented, and is now implemented. (I didn't have time to get to
the couple of enhancements suggested by Masami, but I expect to be
able to add those later on top of these.)
Because we now support compound keys and it's not immediately clear in
the output exactly which fields correspond to keys, the key(s),
compound or not, are now enclosed by curly braces.
The Documentation and README have been updated to reflect the changes,
and several new examples have been added to illustrate how to use
compound keys.
Also, the code was updated to work with the new ftrace_event_file,
etc, renaming in tracing/for-next.
Changes from v4:
This version addresses some problems and suggestions made by Daniel
Wagner - a lot of the code was reworked to get rid of the distinction
between keys and values, and as a result, both keys and values can be
used as sort keys. As suggested, it also allows 'val=' to be absent
in a trigger command - if no 'val' is specified, hitcount is assumed
and automatically used as the only val.
The map code was also separated out into a separate file,
tracing_map.c, allowing it to be reused. It also adds a second tracer
called function_hist that actually does reuse the code, as an RFC
patch.
Patch 01/10 [tracing: Update cond flag when enabling or disabling..]
is a fix for a problem noticed by Daniel and that fixes a problem in
existing trigger code and should be applied regardless of whether the
rest of the patchset is merged.
As mentioned, patch 10/10 is an RFC patch implementing a new tracer
based on the function tracer code. It's a fun little tool and is
useful for a specific problem I'm working on (and is also a nice test
of the tracing_map code), but is an RFC because first, I'm not sure it
would really be of general interest and secondly, it's POC-level
quality and I'd need to spend more time fixing it up to make it
upstreamable, but I don't want to waste my time if not.
There are a couple of important bits of functionality that were
present in v1 but not yet reimplemented in v5.
The first is support for compound keys. Currently, maps can only be
keyed on a single event field, whereas in v1 they could be keyed on
multiple keys. With support for compound keys, you can create much
more interesting output, such as for example per-pid lists of
syscalls or read counts e.g.:
# echo 'hist:keys=common_pid.execname,id.syscall:vals=hitcount' > \
/sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/trigger
# cat /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/hist
key: common_pid:bash[3112], id:sys_write vals: count:69
key: common_pid:bash[3112], id:sys_rt_sigprocmask vals: count:218
key: common_pid:update-notifier[3164], id:sys_poll vals: count:37
key: common_pid:update-notifier[3164], id:sys_recvfrom vals: count:118
key: common_pid:deja-dup-monito[3194], id:sys_sendto vals: count:1
key: common_pid:deja-dup-monito[3194], id:sys_read vals: count:4
key: common_pid:deja-dup-monito[3194], id:sys_poll vals: count:8
key: common_pid:deja-dup-monito[3194], id:sys_recvmsg vals: count:8
key: common_pid:deja-dup-monito[3194], id:sys_getegid vals: count:8
key: common_pid:emacs[3275], id:sys_fsync vals: count:1
key: common_pid:emacs[3275], id:sys_open vals: count:1
key: common_pid:emacs[3275], id:sys_symlink vals: count:2
key: common_pid:emacs[3275], id:sys_poll vals: count:23
key: common_pid:emacs[3275], id:sys_select vals: count:23
key: common_pid:emacs[3275], id:unknown_syscall vals: count:34
key: common_pid:emacs[3275], id:sys_ioctl vals: count:60
key: common_pid:emacs[3275], id:sys_rt_sigprocmask vals: count:116
key: common_pid:cat[3323], id:sys_munmap vals: count:1
key: common_pid:cat[3323], id:sys_fadvise64 vals: count:1
Related to that is support for sorting on multiple fields. Currently,
you can sort using only a primary key. Being able to sort on multiple
or at least a secondary key is indispensible for seeing trends when
displaying multiple values.
Changes from v3:
v4 fixes the race in tracing_map_insert() noted in v3, where
map.val.key could be checked even if map.val wasn't yet set. The
simple fix for that in tracing_map_insert() introduces the possibility
of duplicates in the map, which though rare, need to be accounted for
in the output. To address that, duplicate-merging code was added to
the map-printing code.
It was also pointed out that it didn't seem correct to include
module.h, but the fix for that has deeper roots and is being addressed
by a separate patchset; for now we need to continue including
module.h, though prompted by that I did some other header include
cleanup.
The functionality remains the same as v2, but this version no longer
tries to export and use bpf_maps, and more importantly removes the
associated GFP_NOTRACE/trace event hacks and kmem macros required to
work around the bpf_map implementation.
The tracing_map functionality is instead built on top of a simple
lock-free map algorithm originated by Dr. Cliff Click (see references
in the code for more details), which though too restrictive to be
general-purpose in its current form, functions nicely as a
special-purpose tracing map.
v3 also moves the hist triggers code into a separate file and puts it
all behind a new config option, CONFIG_HIST_TRIGGERS. It also merges
in the sorting code rather than keeping it as a separate patch.
This patchset also includes a couple other new and related triggers,
enable_hist and disable_hist, very similar to the existing
enable_event/disable_event triggers used to automatically enable and
disable events based on a triggering condition, but in this case
allowing hist triggers to be enabled and disabled in the same way.
- Added an insert check for val before checking the key associated with val
- Added code to merge possible duplicates in the map
Changes from v2:
- reimplemented tracing_map, replacing bpf_map with nmi-safe/lock-free map
- removed GPF_NOTRACE, kmalloc/free macros and event hacks needed by bpf_maps
- moved hist triggers from trace_events_trigger.c to trace_events_hist.c
- added CONFIG_HIST_TRIGGERS config option
- consolidated sorting code with main patch
Changes from v1:
- completely rewritten on top of tracing_map (renamed and exported bpf_map)
- added map clearing and client ops to tracing_map
- changed the name from 'hash' triggers to 'hist' triggers
- added new trigger 'pause' feature
- added new enable_hist and disable_hist triggers
- added usage for hist/enable_hist/disable hist to tracing/README
- moved examples into Documentation/trace/event.txt
- added ___GFP_NOTRACE, kmalloc/kfree macros, and conditional kmem tracepoints
The following changes since commit e428abbbf616cd8fdd1162e4a624ad1d47b47544:
tracing: #ifdef out uses of max trace when CONFIG_TRACER_MAX_TRACE is not set (2015-11-10 10:16:05 -0500)
are available in the git repository at:
git://git.yoctoproject.org/linux-yocto-contrib.git tzanussi/hist-triggers-v12
http://git.yoctoproject.org/cgit/cgit.cgi/linux-yocto-contrib/log/?h=tzanussi/hist-triggers-v12
Masami Hiramatsu (2):
kselftests/ftrace : Add event trigger testcases
kselftests/ftrace: Add hist trigger testcases
Namhyung Kim (2):
tracing: Support string type key properly
tracing: Add hist trigger 'log2' modifier
Tom Zanussi (26):
tracing: Update cond flag when enabling or disabling a trigger
tracing: Make ftrace_event_field checking functions available
tracing: Make event trigger functions available
tracing: Add event record param to trigger_ops.func()
tracing: Add get_syscall_name()
tracing: Add a per-event-trigger 'paused' field
tracing: Add needs_rec flag to event triggers
tracing: Add an unreg_all() callback to trigger commands
tracing: Add lock-free tracing_map
tracing: Add 'hist' event trigger command
tracing: Add hist trigger support for multiple values ('vals=' param)
tracing: Add hist trigger support for compound keys
tracing: Add hist trigger support for user-defined sorting ('sort='
param)
tracing: Add hist trigger support for pausing and continuing a trace
tracing: Add hist trigger support for clearing a trace
tracing: Add hist trigger 'hex' modifier for displaying numeric fields
tracing: Add hist trigger 'sym' and 'sym-offset' modifiers
tracing: Add hist trigger 'execname' modifier
tracing: Add hist trigger 'syscall' modifier
tracing: Add hist trigger support for stacktraces as keys
tracing: Remove restriction on string position in hist trigger keys
tracing: Add enable_hist/disable_hist triggers
tracing: Add 'hist' trigger Documentation
tracing: Add support for multiple hist triggers per event
tracing: Add support for named triggers
tracing: Add support for named hist triggers
Documentation/trace/events.txt | 1555 ++++++++++++++++++
include/linux/trace_events.h | 9 +-
kernel/trace/Kconfig | 27 +
kernel/trace/Makefile | 2 +
kernel/trace/trace.c | 57 +
kernel/trace/trace.h | 106 +-
kernel/trace/trace_events.c | 4 +
kernel/trace/trace_events_filter.c | 12 -
kernel/trace/trace_events_hist.c | 1731 ++++++++++++++++++++
kernel/trace/trace_events_trigger.c | 309 +++-
kernel/trace/trace_syscalls.c | 11 +
kernel/trace/tracing_map.c | 1058 ++++++++++++
kernel/trace/tracing_map.h | 282 ++++
tools/testing/selftests/ftrace/test.d/functions | 9 +
.../ftrace/test.d/trigger/trigger-eventonoff.tc | 64 +
.../ftrace/test.d/trigger/trigger-filter.tc | 59 +
.../ftrace/test.d/trigger/trigger-hist-mod.tc | 65 +
.../ftrace/test.d/trigger/trigger-hist.tc | 83 +
.../ftrace/test.d/trigger/trigger-multihist.tc | 73 +
.../ftrace/test.d/trigger/trigger-snapshot.tc | 56 +
.../ftrace/test.d/trigger/trigger-stacktrace.tc | 53 +
.../ftrace/test.d/trigger/trigger-traceonoff.tc | 58 +
22 files changed, 5596 insertions(+), 87 deletions(-)
create mode 100644 kernel/trace/trace_events_hist.c
create mode 100644 kernel/trace/tracing_map.c
create mode 100644 kernel/trace/tracing_map.h
create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc
create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc
create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc
create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc
create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc
create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc
create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc
create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:00 +0100 |
| Subject | [PATCH v12 02/30] tracing: Make ftrace_event_field checking functions available |
| Message-ID | <qy5rm-6jA-61@gated-at.bofh.it> |
| In reply to | #1275802 |
Make is_string_field() and is_function_field() accessible outside of
trace_event_filters.c for other users of ftrace_event_fields.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
kernel/trace/trace.h | 12 ++++++++++++
kernel/trace/trace_events_filter.c | 12 ------------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 919d9d0..fd22cd4 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1113,6 +1113,18 @@ struct filter_pred {
unsigned short right;
};
+static inline bool is_string_field(struct ftrace_event_field *field)
+{
+ return field->filter_type == FILTER_DYN_STRING ||
+ field->filter_type == FILTER_STATIC_STRING ||
+ field->filter_type == FILTER_PTR_STRING;
+}
+
+static inline bool is_function_field(struct ftrace_event_field *field)
+{
+ return field->filter_type == FILTER_TRACE_FN;
+}
+
extern enum regex_type
filter_parse_regex(char *buff, int len, char **search, int *not);
extern void print_event_filter(struct trace_event_file *file,
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index f93a219..b5ca7eb 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -961,18 +961,6 @@ int filter_assign_type(const char *type)
return FILTER_OTHER;
}
-static bool is_function_field(struct ftrace_event_field *field)
-{
- return field->filter_type == FILTER_TRACE_FN;
-}
-
-static bool is_string_field(struct ftrace_event_field *field)
-{
- return field->filter_type == FILTER_DYN_STRING ||
- field->filter_type == FILTER_STATIC_STRING ||
- field->filter_type == FILTER_PTR_STRING;
-}
-
static bool is_legal_op(struct ftrace_event_field *field, int op)
{
if (is_string_field(field) &&
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:00 +0100 |
| Subject | [PATCH v12 26/30] tracing: Add support for named triggers |
| Message-ID | <qy5rm-6jA-63@gated-at.bofh.it> |
| In reply to | #1275802 |
Named triggers are sets of triggers that share a common set of trigger
data. An example of functionality that could benefit from this type
of capability would be a set of inlined probes that would each
contribute event counts, for example, to a shared counter data
structure.
The first named trigger registered with a given name owns the common
trigger data that the others subsequently registered with the same
name will reference. The functions defined here allow users to add,
delete, and find named triggers.
It also adds functions to pause and unpause named triggers; since
named triggers act upon common data, they should also be paused and
unpaused as a group.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
kernel/trace/trace.h | 13 ++++
kernel/trace/trace_events_trigger.c | 143 ++++++++++++++++++++++++++++++++++++
2 files changed, 156 insertions(+)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 7126dd7..fed2ae0 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1183,7 +1183,11 @@ struct event_trigger_data {
char *filter_str;
void *private_data;
bool paused;
+ bool paused_tmp;
struct list_head list;
+ char *name;
+ struct list_head named_list;
+ struct event_trigger_data *named_data;
};
/* Avoid typos */
@@ -1226,6 +1230,15 @@ extern void unregister_trigger(char *glob, struct event_trigger_ops *ops,
extern int set_trigger_filter(char *filter_str,
struct event_trigger_data *trigger_data,
struct trace_event_file *file);
+extern struct event_trigger_data *find_named_trigger(const char *name);
+extern bool is_named_trigger(struct event_trigger_data *test);
+extern int save_named_trigger(const char *name,
+ struct event_trigger_data *data);
+extern void del_named_trigger(struct event_trigger_data *data);
+extern void pause_named_trigger(struct event_trigger_data *data);
+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 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_trigger.c b/kernel/trace/trace_events_trigger.c
index 2d057f2..c06593e 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -646,6 +646,7 @@ event_trigger_callback(struct event_command *cmd_ops,
trigger_data->ops = trigger_ops;
trigger_data->cmd_ops = cmd_ops;
INIT_LIST_HEAD(&trigger_data->list);
+ INIT_LIST_HEAD(&trigger_data->named_list);
if (glob[0] == '!') {
cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
@@ -769,6 +770,148 @@ int set_trigger_filter(char *filter_str,
return ret;
}
+static LIST_HEAD(named_triggers);
+
+/**
+ * find_named_trigger - Find the common named trigger associated with @name
+ * @name: The name of the set of named triggers to find the common data for
+ *
+ * Named triggers are sets of triggers that share a common set of
+ * trigger data. The first named trigger registered with a given name
+ * owns the common trigger data that the others subsequently
+ * registered with the same name will reference. This function
+ * returns the common trigger data associated with that first
+ * registered instance.
+ *
+ * Return: the common trigger data for the given named trigger on
+ * success, NULL otherwise.
+ */
+struct event_trigger_data *find_named_trigger(const char *name)
+{
+ struct event_trigger_data *data;
+
+ if (!name)
+ return NULL;
+
+ list_for_each_entry(data, &named_triggers, named_list) {
+ if (data->named_data)
+ continue;
+ if (!strcmp(data->name, name))
+ return data;
+ }
+
+ return NULL;
+}
+
+/**
+ * is_named_trigger - determine if a given trigger is a named trigger
+ * @test: The trigger data to test
+ *
+ * Return: true if 'test' is a named trigger, false otherwise.
+ */
+bool is_named_trigger(struct event_trigger_data *test)
+{
+ struct event_trigger_data *data;
+
+ list_for_each_entry(data, &named_triggers, named_list) {
+ if (test == data)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * save_named_trigger - save the trigger in the named trigger list
+ * @name: The name of the named trigger set
+ * @data: The trigger data to save
+ *
+ * Return: 0 if successful, negative error otherwise.
+ */
+int save_named_trigger(const char *name, struct event_trigger_data *data)
+{
+ data->name = kstrdup(name, GFP_KERNEL);
+ if (!data->name)
+ return -ENOMEM;
+
+ list_add(&data->named_list, &named_triggers);
+
+ return 0;
+}
+
+/**
+ * del_named_trigger - delete a trigger from the named trigger list
+ * @data: The trigger data to delete
+ */
+void del_named_trigger(struct event_trigger_data *data)
+{
+ kfree(data->name);
+ data->name = NULL;
+
+ list_del(&data->named_list);
+}
+
+static void __pause_named_trigger(struct event_trigger_data *data, bool pause)
+{
+ struct event_trigger_data *test;
+
+ list_for_each_entry(test, &named_triggers, named_list) {
+ if (!strcmp(test->name, data->name)) {
+ if (pause) {
+ test->paused_tmp = test->paused;
+ test->paused = true;
+ } else {
+ test->paused = test->paused_tmp;
+ }
+ }
+ }
+}
+
+/**
+ * pause_named_trigger - Pause all named triggers with the same name
+ * @data: The trigger data of a named trigger to pause
+ *
+ * Pauses a named trigger along with all other triggers having the
+ * same name. Because named triggers share a common set of data,
+ * pausing only one is meaningless, so pausing one named trigger needs
+ * to pause all triggers with the same name.
+ */
+void pause_named_trigger(struct event_trigger_data *data)
+{
+ __pause_named_trigger(data, true);
+}
+
+/**
+ * unpause_named_trigger - Un-pause all named triggers with the same name
+ * @data: The trigger data of a named trigger to unpause
+ *
+ * Un-pauses a named trigger along with all other triggers having the
+ * same name. Because named triggers share a common set of data,
+ * unpausing only one is meaningless, so unpausing one named trigger
+ * needs to unpause all triggers with the same name.
+ */
+void unpause_named_trigger(struct event_trigger_data *data)
+{
+ __pause_named_trigger(data, false);
+}
+
+/**
+ * set_named_trigger_data - Associate common named trigger data
+ * @data: The trigger data of a named trigger to unpause
+ *
+ * Named triggers are sets of triggers that share a common set of
+ * trigger data. The first named trigger registered with a given name
+ * owns the common trigger data that the others subsequently
+ * registered with the same name will reference. This function
+ * associates the common trigger data from the first trigger with the
+ * given trigger.
+ */
+void set_named_trigger_data(struct event_trigger_data *data,
+ struct event_trigger_data *named_data)
+{
+ data->named_data = named_data;
+}
+
static void
traceon_trigger(struct event_trigger_data *data, void *rec)
{
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:00 +0100 |
| Subject | [PATCH v12 04/30] tracing: Add event record param to trigger_ops.func() |
| Message-ID | <qy5rm-6jA-75@gated-at.bofh.it> |
| In reply to | #1275802 |
Some triggers may need access to the trace event, so pass it in. Also
fix up the existing trigger funcs and their callers.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
include/linux/trace_events.h | 7 ++++---
kernel/trace/trace.h | 6 ++++--
kernel/trace/trace_events_trigger.c | 36 +++++++++++++++++++-----------------
3 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 429fdfc..95e8fef 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -430,7 +430,8 @@ extern int call_filter_check_discard(struct trace_event_call *call, 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);
+ enum event_trigger_type tt,
+ void *rec);
bool trace_event_ignore_this_pid(struct trace_event_file *trace_file);
@@ -517,7 +518,7 @@ event_trigger_unlock_commit(struct trace_event_file *file,
trace_buffer_unlock_commit(file->tr, buffer, event, irq_flags, pc);
if (tt)
- event_triggers_post_call(file, tt);
+ event_triggers_post_call(file, tt, entry);
}
/**
@@ -550,7 +551,7 @@ event_trigger_unlock_commit_regs(struct trace_event_file *file,
irq_flags, pc, regs);
if (tt)
- event_triggers_post_call(file, tt);
+ event_triggers_post_call(file, tt, entry);
}
#ifdef CONFIG_BPF_EVENTS
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 2aa03be..c8cf683 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1202,7 +1202,8 @@ extern int register_event_command(struct event_command *cmd);
* @func: The trigger 'probe' function called when the triggering
* event occurs. The data passed into this callback is the data
* that was supplied to the event_command @reg() function that
- * registered the trigger (see struct event_command).
+ * registered the trigger (see struct event_command) along with
+ * the trace record, rec.
*
* @init: An optional initialization function called for the trigger
* when the trigger is registered (via the event_command reg()
@@ -1227,7 +1228,8 @@ extern int register_event_command(struct event_command *cmd);
* (see trace_event_triggers.c).
*/
struct event_trigger_ops {
- void (*func)(struct event_trigger_data *data);
+ void (*func)(struct event_trigger_data *data,
+ void *rec);
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_trigger.c b/kernel/trace/trace_events_trigger.c
index 6087052..c3abef6 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -73,7 +73,7 @@ event_triggers_call(struct trace_event_file *file, void *rec)
list_for_each_entry_rcu(data, &file->triggers, list) {
if (!rec) {
- data->ops->func(data);
+ data->ops->func(data, rec);
continue;
}
filter = rcu_dereference_sched(data->filter);
@@ -83,7 +83,7 @@ event_triggers_call(struct trace_event_file *file, void *rec)
tt |= data->cmd_ops->trigger_type;
continue;
}
- data->ops->func(data);
+ data->ops->func(data, rec);
}
return tt;
}
@@ -93,6 +93,7 @@ EXPORT_SYMBOL_GPL(event_triggers_call);
* event_triggers_post_call - Call 'post_triggers' for a trace event
* @file: The trace_event_file associated with the event
* @tt: enum event_trigger_type containing a set bit for each trigger to invoke
+ * @rec: The trace entry for the event
*
* For each trigger associated with an event, invoke the trigger
* function registered with the associated trigger command, if the
@@ -103,13 +104,14 @@ EXPORT_SYMBOL_GPL(event_triggers_call);
*/
void
event_triggers_post_call(struct trace_event_file *file,
- enum event_trigger_type tt)
+ enum event_trigger_type tt,
+ void *rec)
{
struct event_trigger_data *data;
list_for_each_entry_rcu(data, &file->triggers, list) {
if (data->cmd_ops->trigger_type & tt)
- data->ops->func(data);
+ data->ops->func(data, rec);
}
}
EXPORT_SYMBOL_GPL(event_triggers_post_call);
@@ -750,7 +752,7 @@ int set_trigger_filter(char *filter_str,
}
static void
-traceon_trigger(struct event_trigger_data *data)
+traceon_trigger(struct event_trigger_data *data, void *rec)
{
if (tracing_is_on())
return;
@@ -759,7 +761,7 @@ traceon_trigger(struct event_trigger_data *data)
}
static void
-traceon_count_trigger(struct event_trigger_data *data)
+traceon_count_trigger(struct event_trigger_data *data, void *rec)
{
if (tracing_is_on())
return;
@@ -774,7 +776,7 @@ traceon_count_trigger(struct event_trigger_data *data)
}
static void
-traceoff_trigger(struct event_trigger_data *data)
+traceoff_trigger(struct event_trigger_data *data, void *rec)
{
if (!tracing_is_on())
return;
@@ -783,7 +785,7 @@ traceoff_trigger(struct event_trigger_data *data)
}
static void
-traceoff_count_trigger(struct event_trigger_data *data)
+traceoff_count_trigger(struct event_trigger_data *data, void *rec)
{
if (!tracing_is_on())
return;
@@ -879,13 +881,13 @@ static struct event_command trigger_traceoff_cmd = {
#ifdef CONFIG_TRACER_SNAPSHOT
static void
-snapshot_trigger(struct event_trigger_data *data)
+snapshot_trigger(struct event_trigger_data *data, void *rec)
{
tracing_snapshot();
}
static void
-snapshot_count_trigger(struct event_trigger_data *data)
+snapshot_count_trigger(struct event_trigger_data *data, void *rec)
{
if (!data->count)
return;
@@ -893,7 +895,7 @@ snapshot_count_trigger(struct event_trigger_data *data)
if (data->count != -1)
(data->count)--;
- snapshot_trigger(data);
+ snapshot_trigger(data, rec);
}
static int
@@ -972,13 +974,13 @@ static __init int register_trigger_snapshot_cmd(void) { return 0; }
#define STACK_SKIP 3
static void
-stacktrace_trigger(struct event_trigger_data *data)
+stacktrace_trigger(struct event_trigger_data *data, void *rec)
{
trace_dump_stack(STACK_SKIP);
}
static void
-stacktrace_count_trigger(struct event_trigger_data *data)
+stacktrace_count_trigger(struct event_trigger_data *data, void *rec)
{
if (!data->count)
return;
@@ -986,7 +988,7 @@ stacktrace_count_trigger(struct event_trigger_data *data)
if (data->count != -1)
(data->count)--;
- stacktrace_trigger(data);
+ stacktrace_trigger(data, rec);
}
static int
@@ -1057,7 +1059,7 @@ struct enable_trigger_data {
};
static void
-event_enable_trigger(struct event_trigger_data *data)
+event_enable_trigger(struct event_trigger_data *data, void *rec)
{
struct enable_trigger_data *enable_data = data->private_data;
@@ -1068,7 +1070,7 @@ event_enable_trigger(struct event_trigger_data *data)
}
static void
-event_enable_count_trigger(struct event_trigger_data *data)
+event_enable_count_trigger(struct event_trigger_data *data, void *rec)
{
struct enable_trigger_data *enable_data = data->private_data;
@@ -1082,7 +1084,7 @@ event_enable_count_trigger(struct event_trigger_data *data)
if (data->count != -1)
(data->count)--;
- event_enable_trigger(data);
+ event_enable_trigger(data, rec);
}
static int
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:00 +0100 |
| Subject | [PATCH v12 23/30] tracing: Add enable_hist/disable_hist triggers |
| Message-ID | <qy5rm-6jA-67@gated-at.bofh.it> |
| In reply to | #1275802 |
Similar to enable_event/disable_event triggers, these triggers enable
and disable the aggregation of events into maps rather than enabling
and disabling their writing into the trace buffer.
They can be used to automatically start and stop hist triggers based
on a matching filter condition.
If there's a paused hist trigger on system:event, the following would
start it when the filter condition was hit:
# echo enable_hist:system:event [ if filter] > event/trigger
And the following would disable a running system:event hist trigger:
# echo disable_hist:system:event [ if filter] > event/trigger
See Documentation/trace/events.txt for real examples.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
include/linux/trace_events.h | 1 +
kernel/trace/trace.c | 8 +++
kernel/trace/trace.h | 32 ++++++++++
kernel/trace/trace_events_hist.c | 115 ++++++++++++++++++++++++++++++++++++
kernel/trace/trace_events_trigger.c | 71 ++++++++++++----------
5 files changed, 196 insertions(+), 31 deletions(-)
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index e61816d..70f8fc4 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -418,6 +418,7 @@ enum event_trigger_type {
ETT_STACKTRACE = (1 << 2),
ETT_EVENT_ENABLE = (1 << 3),
ETT_EVENT_HIST = (1 << 4),
+ ETT_HIST_ENABLE = (1 << 5),
};
extern int filter_match_preds(struct event_filter *filter, void *rec);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 1ddeee5..8b9424d 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3798,6 +3798,10 @@ static const char readme_msg[] =
"\t trigger: traceon, traceoff\n"
"\t enable_event:<system>:<event>\n"
"\t disable_event:<system>:<event>\n"
+#ifdef CONFIG_HIST_TRIGGERS
+ "\t enable_hist:<system>:<event>\n"
+ "\t disable_hist:<system>:<event>\n"
+#endif
#ifdef CONFIG_STACKTRACE
"\t\t stacktrace\n"
#endif
@@ -3858,6 +3862,10 @@ static const char readme_msg[] =
"\t The 'clear' parameter will clear the contents of a running\n"
"\t hist trigger and leave its current paused/active state\n"
"\t unchanged.\n\n"
+ "\t The enable_hist and disable_hist triggers can be used to\n"
+ "\t have one event conditionally start and stop another event's\n"
+ "\t already-attached hist trigger. The syntax is analagous to\n"
+ "\t the enable_event and disable_event triggers.\n"
#endif
;
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index a2a6793..7126dd7 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1165,8 +1165,10 @@ extern const struct file_operations event_hist_fops;
#ifdef CONFIG_HIST_TRIGGERS
extern int register_trigger_hist_cmd(void);
+extern int register_trigger_hist_enable_disable_cmds(void);
#else
static inline int register_trigger_hist_cmd(void) { return 0; }
+static inline int register_trigger_hist_enable_disable_cmds(void) { return 0; }
#endif
extern int register_trigger_cmds(void);
@@ -1184,6 +1186,34 @@ struct event_trigger_data {
struct list_head list;
};
+/* Avoid typos */
+#define ENABLE_EVENT_STR "enable_event"
+#define DISABLE_EVENT_STR "disable_event"
+#define ENABLE_HIST_STR "enable_hist"
+#define DISABLE_HIST_STR "disable_hist"
+
+struct enable_trigger_data {
+ struct trace_event_file *file;
+ bool enable;
+ bool hist;
+};
+
+extern int event_enable_trigger_print(struct seq_file *m,
+ struct event_trigger_ops *ops,
+ struct event_trigger_data *data);
+extern void event_enable_trigger_free(struct event_trigger_ops *ops,
+ struct event_trigger_data *data);
+extern int event_enable_trigger_func(struct event_command *cmd_ops,
+ struct trace_event_file *file,
+ char *glob, char *cmd, char *param);
+extern int event_enable_register_trigger(char *glob,
+ struct event_trigger_ops *ops,
+ struct event_trigger_data *data,
+ struct trace_event_file *file);
+extern void event_enable_unregister_trigger(char *glob,
+ struct event_trigger_ops *ops,
+ struct event_trigger_data *test,
+ struct trace_event_file *file);
extern void trigger_data_free(struct event_trigger_data *data);
extern int event_trigger_init(struct event_trigger_ops *ops,
struct event_trigger_data *data);
@@ -1197,6 +1227,8 @@ extern int set_trigger_filter(char *filter_str,
struct event_trigger_data *trigger_data,
struct trace_event_file *file);
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);
/**
* struct event_trigger_ops - callbacks for trace event triggers
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 90a7c93..49bd1f3 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -1372,3 +1372,118 @@ __init int register_trigger_hist_cmd(void)
return ret;
}
+
+static void
+hist_enable_trigger(struct event_trigger_data *data, void *rec)
+{
+ struct enable_trigger_data *enable_data = data->private_data;
+ struct event_trigger_data *test;
+
+ list_for_each_entry_rcu(test, &enable_data->file->triggers, list) {
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+ if (enable_data->enable)
+ test->paused = false;
+ else
+ test->paused = true;
+ break;
+ }
+ }
+}
+
+static void
+hist_enable_count_trigger(struct event_trigger_data *data, void *rec)
+{
+ if (!data->count)
+ return;
+
+ if (data->count != -1)
+ (data->count)--;
+
+ hist_enable_trigger(data, rec);
+}
+
+static struct event_trigger_ops hist_enable_trigger_ops = {
+ .func = hist_enable_trigger,
+ .print = event_enable_trigger_print,
+ .init = event_trigger_init,
+ .free = event_enable_trigger_free,
+};
+
+static struct event_trigger_ops hist_enable_count_trigger_ops = {
+ .func = hist_enable_count_trigger,
+ .print = event_enable_trigger_print,
+ .init = event_trigger_init,
+ .free = event_enable_trigger_free,
+};
+
+static struct event_trigger_ops hist_disable_trigger_ops = {
+ .func = hist_enable_trigger,
+ .print = event_enable_trigger_print,
+ .init = event_trigger_init,
+ .free = event_enable_trigger_free,
+};
+
+static struct event_trigger_ops hist_disable_count_trigger_ops = {
+ .func = hist_enable_count_trigger,
+ .print = event_enable_trigger_print,
+ .init = event_trigger_init,
+ .free = event_enable_trigger_free,
+};
+
+static struct event_trigger_ops *
+hist_enable_get_trigger_ops(char *cmd, char *param)
+{
+ struct event_trigger_ops *ops;
+ bool enable;
+
+ enable = (strcmp(cmd, ENABLE_HIST_STR) == 0);
+
+ if (enable)
+ ops = param ? &hist_enable_count_trigger_ops :
+ &hist_enable_trigger_ops;
+ else
+ ops = param ? &hist_disable_count_trigger_ops :
+ &hist_disable_trigger_ops;
+
+ return ops;
+}
+
+static struct event_command trigger_hist_enable_cmd = {
+ .name = ENABLE_HIST_STR,
+ .trigger_type = ETT_HIST_ENABLE,
+ .func = event_enable_trigger_func,
+ .reg = event_enable_register_trigger,
+ .unreg = event_enable_unregister_trigger,
+ .get_trigger_ops = hist_enable_get_trigger_ops,
+ .set_filter = set_trigger_filter,
+};
+
+static struct event_command trigger_hist_disable_cmd = {
+ .name = DISABLE_HIST_STR,
+ .trigger_type = ETT_HIST_ENABLE,
+ .func = event_enable_trigger_func,
+ .reg = event_enable_register_trigger,
+ .unreg = event_enable_unregister_trigger,
+ .get_trigger_ops = hist_enable_get_trigger_ops,
+ .set_filter = set_trigger_filter,
+};
+
+static __init void unregister_trigger_hist_enable_disable_cmds(void)
+{
+ unregister_event_command(&trigger_hist_enable_cmd);
+ unregister_event_command(&trigger_hist_disable_cmd);
+}
+
+__init int register_trigger_hist_enable_disable_cmds(void)
+{
+ int ret;
+
+ ret = register_event_command(&trigger_hist_enable_cmd);
+ if (WARN_ON(ret < 0))
+ return ret;
+ ret = register_event_command(&trigger_hist_disable_cmd);
+ if (WARN_ON(ret < 0))
+ unregister_trigger_hist_enable_disable_cmds();
+
+ return ret;
+}
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 1f1db3c..2d057f2 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -352,7 +352,7 @@ __init int register_event_command(struct event_command *cmd)
* Currently we only unregister event commands from __init, so mark
* this __init too.
*/
-static __init int unregister_event_command(struct event_command *cmd)
+__init int unregister_event_command(struct event_command *cmd)
{
struct event_command *p, *n;
int ret = -ENODEV;
@@ -1067,15 +1067,6 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)
unregister_event_command(&trigger_traceoff_cmd);
}
-/* Avoid typos */
-#define ENABLE_EVENT_STR "enable_event"
-#define DISABLE_EVENT_STR "disable_event"
-
-struct enable_trigger_data {
- struct trace_event_file *file;
- bool enable;
-};
-
static void
event_enable_trigger(struct event_trigger_data *data, void *rec)
{
@@ -1105,14 +1096,16 @@ event_enable_count_trigger(struct event_trigger_data *data, void *rec)
event_enable_trigger(data, rec);
}
-static int
-event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops,
- struct event_trigger_data *data)
+int event_enable_trigger_print(struct seq_file *m,
+ struct event_trigger_ops *ops,
+ struct event_trigger_data *data)
{
struct enable_trigger_data *enable_data = data->private_data;
seq_printf(m, "%s:%s:%s",
- enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR,
+ enable_data->hist ?
+ (enable_data->enable ? ENABLE_HIST_STR : DISABLE_HIST_STR) :
+ (enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR),
enable_data->file->event_call->class->system,
trace_event_name(enable_data->file->event_call));
@@ -1129,9 +1122,8 @@ event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops,
return 0;
}
-static void
-event_enable_trigger_free(struct event_trigger_ops *ops,
- struct event_trigger_data *data)
+void event_enable_trigger_free(struct event_trigger_ops *ops,
+ struct event_trigger_data *data)
{
struct enable_trigger_data *enable_data = data->private_data;
@@ -1176,10 +1168,9 @@ static struct event_trigger_ops event_disable_count_trigger_ops = {
.free = event_enable_trigger_free,
};
-static int
-event_enable_trigger_func(struct event_command *cmd_ops,
- struct trace_event_file *file,
- char *glob, char *cmd, char *param)
+int event_enable_trigger_func(struct event_command *cmd_ops,
+ struct trace_event_file *file,
+ char *glob, char *cmd, char *param)
{
struct trace_event_file *event_enable_file;
struct enable_trigger_data *enable_data;
@@ -1188,6 +1179,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,
struct trace_array *tr = file->tr;
const char *system;
const char *event;
+ bool hist = false;
char *trigger;
char *number;
bool enable;
@@ -1212,8 +1204,15 @@ event_enable_trigger_func(struct event_command *cmd_ops,
if (!event_enable_file)
goto out;
- enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
+#ifdef CONFIG_HIST_TRIGGERS
+ hist = ((strcmp(cmd, ENABLE_HIST_STR) == 0) ||
+ (strcmp(cmd, DISABLE_HIST_STR) == 0));
+ enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) ||
+ (strcmp(cmd, ENABLE_HIST_STR) == 0));
+#else
+ enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
+#endif
trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger);
ret = -ENOMEM;
@@ -1233,6 +1232,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,
INIT_LIST_HEAD(&trigger_data->list);
RCU_INIT_POINTER(trigger_data->filter, NULL);
+ enable_data->hist = hist;
enable_data->enable = enable;
enable_data->file = event_enable_file;
trigger_data->private_data = enable_data;
@@ -1310,10 +1310,10 @@ event_enable_trigger_func(struct event_command *cmd_ops,
goto out;
}
-static int event_enable_register_trigger(char *glob,
- struct event_trigger_ops *ops,
- struct event_trigger_data *data,
- struct trace_event_file *file)
+int event_enable_register_trigger(char *glob,
+ struct event_trigger_ops *ops,
+ struct event_trigger_data *data,
+ struct trace_event_file *file)
{
struct enable_trigger_data *enable_data = data->private_data;
struct enable_trigger_data *test_enable_data;
@@ -1323,6 +1323,8 @@ static int event_enable_register_trigger(char *glob,
list_for_each_entry_rcu(test, &file->triggers, list) {
test_enable_data = test->private_data;
if (test_enable_data &&
+ (test->cmd_ops->trigger_type ==
+ data->cmd_ops->trigger_type) &&
(test_enable_data->file == enable_data->file)) {
ret = -EEXIST;
goto out;
@@ -1348,10 +1350,10 @@ out:
return ret;
}
-static void event_enable_unregister_trigger(char *glob,
- struct event_trigger_ops *ops,
- struct event_trigger_data *test,
- struct trace_event_file *file)
+void event_enable_unregister_trigger(char *glob,
+ struct event_trigger_ops *ops,
+ struct event_trigger_data *test,
+ struct trace_event_file *file)
{
struct enable_trigger_data *test_enable_data = test->private_data;
struct enable_trigger_data *enable_data;
@@ -1361,6 +1363,8 @@ static void event_enable_unregister_trigger(char *glob,
list_for_each_entry_rcu(data, &file->triggers, list) {
enable_data = data->private_data;
if (enable_data &&
+ (data->cmd_ops->trigger_type ==
+ test->cmd_ops->trigger_type) &&
(enable_data->file == test_enable_data->file)) {
unregistered = true;
list_del_rcu(&data->list);
@@ -1380,8 +1384,12 @@ event_enable_get_trigger_ops(char *cmd, char *param)
struct event_trigger_ops *ops;
bool enable;
+#ifdef CONFIG_HIST_TRIGGERS
+ enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) ||
+ (strcmp(cmd, ENABLE_HIST_STR) == 0));
+#else
enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
-
+#endif
if (enable)
ops = param ? &event_enable_count_trigger_ops :
&event_enable_trigger_ops;
@@ -1452,6 +1460,7 @@ __init int register_trigger_cmds(void)
register_trigger_snapshot_cmd();
register_trigger_stacktrace_cmd();
register_trigger_enable_disable_cmds();
+ register_trigger_hist_enable_disable_cmds();
register_trigger_hist_cmd();
return 0;
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:00 +0100 |
| Subject | [PATCH v12 07/30] tracing: Add needs_rec flag to event triggers |
| Message-ID | <qy5rm-6jA-69@gated-at.bofh.it> |
| In reply to | #1275802 |
Add a new needs_rec flag for triggers that require unconditional
access to trace records in order to function.
Normally a trigger requires access to the contents of a trace record
only if it has a filter associated with it (since filters need the
contents of a record in order to make a filtering decision). Some
types of triggers, such as 'hist' triggers, require access to trace
record contents independent of the presence of filters, so add a new
flag for those triggers.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
kernel/trace/trace.h | 7 +++++++
kernel/trace/trace_events_trigger.c | 3 ++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 5e23a97..300add5 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1293,6 +1293,12 @@ struct event_trigger_ops {
* itself logs to the trace buffer, this flag should be set,
* otherwise it can be left unspecified.
*
+ * @needs_rec: A flag that says whether or not this command needs
+ * access to the trace record in order to perform its function,
+ * regardless of whether or not it has a filter associated with
+ * it (filters make a trigger require access to the trace record
+ * but are not always present).
+ *
* All the methods below, except for @set_filter(), must be
* implemented.
*
@@ -1333,6 +1339,7 @@ struct event_command {
char *name;
enum event_trigger_type trigger_type;
bool post_trigger;
+ bool needs_rec;
int (*func)(struct event_command *cmd_ops,
struct trace_event_file *file,
char *glob, char *cmd, char *params);
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 8375d29..2f881e9 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -498,7 +498,8 @@ void update_cond_flag(struct trace_event_file *file)
bool set_cond = false;
list_for_each_entry_rcu(data, &file->triggers, list) {
- if (data->filter || data->cmd_ops->post_trigger) {
+ if (data->filter || data->cmd_ops->post_trigger ||
+ data->cmd_ops->needs_rec) {
set_cond = true;
break;
}
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:10 +0100 |
| Subject | [PATCH v12 11/30] tracing: Add hist trigger support for multiple values ('vals=' param) |
| Message-ID | <qy5AZ-6Cy-9@gated-at.bofh.it> |
| In reply to | #1275802 |
Allow users to specify trace event fields to use in aggregated sums
via a new 'vals=' keyword. Before this addition, the only aggregated
sum supported was the implied value 'hitcount'. With this addition,
'hitcount' is also supported as an explicit value field, as is any
numeric trace event field.
This expands the hist trigger syntax from this:
# echo hist:keys=xxx [ if filter] > event/trigger
to this:
# echo hist:keys=xxx:vals=yyy [ if filter] > event/trigger
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
kernel/trace/trace.c | 12 ++++---
kernel/trace/trace_events_hist.c | 75 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 81 insertions(+), 6 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 078db8f..0310500 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3825,14 +3825,16 @@ static const char readme_msg[] =
#ifdef CONFIG_HIST_TRIGGERS
" hist trigger\t- If set, event hits are aggregated into a hash table\n"
"\t Format: hist:keys=<field1>\n"
+ "\t [:values=<field1[,field2,...]>]\n"
"\t [:size=#entries]\n"
"\t [if <filter>]\n\n"
"\t When a matching event is hit, an entry is added to a hash\n"
- "\t table using the key named, and the value of a sum called\n"
- "\t 'hitcount' is incremented. Keys correspond to fields in the\n"
- "\t event's format description. Keys can be any field. The\n"
- "\t 'size' parameter can be used to specify more or fewer than\n"
- "\t the default 2048 entries for the hashtable size.\n\n"
+ "\t table using the key(s) and value(s) named, and the value of a\n"
+ "\t sum called 'hitcount' is incremented. Keys and values\n"
+ "\t correspond to fields in the event's format description. Keys\n"
+ "\t can be any field. Values must correspond to numeric fields.\n"
+ "\t The 'size' parameter can be used to specify more or fewer\n"
+ "\t than the default 2048 entries for the hashtable size.\n\n"
"\t Reading the 'hist' file for the event will dump the hash\n"
"\t table in its entirety to stdout."
#endif
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 3c86ab2..389ffa0 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -84,6 +84,7 @@ enum hist_field_flags {
struct hist_trigger_attrs {
char *keys_str;
+ char *vals_str;
unsigned int map_bits;
};
@@ -165,6 +166,7 @@ static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
return;
kfree(attrs->keys_str);
+ kfree(attrs->vals_str);
kfree(attrs);
}
@@ -183,6 +185,10 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
if (!strncmp(str, "keys", strlen("keys")) ||
!strncmp(str, "key", strlen("key")))
attrs->keys_str = kstrdup(str, GFP_KERNEL);
+ else if (!strncmp(str, "values", strlen("values")) ||
+ !strncmp(str, "vals", strlen("vals")) ||
+ !strncmp(str, "val", strlen("val")))
+ attrs->vals_str = kstrdup(str, GFP_KERNEL);
else if (!strncmp(str, "size", strlen("size"))) {
int map_bits = parse_map_size(str);
@@ -271,13 +277,66 @@ static int create_hitcount_val(struct hist_trigger_data *hist_data)
return 0;
}
+static int create_val_field(struct hist_trigger_data *hist_data,
+ unsigned int val_idx,
+ struct trace_event_file *file,
+ char *field_str)
+{
+ struct ftrace_event_field *field = NULL;
+ unsigned long flags = 0;
+ int ret = 0;
+
+ field = trace_find_event_field(file->event_call, field_str);
+ if (!field) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ hist_data->fields[val_idx] = create_hist_field(field, flags);
+ if (!hist_data->fields[val_idx]) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ hist_data->n_vals++;
+ out:
+ return ret;
+}
+
static int create_val_fields(struct hist_trigger_data *hist_data,
struct trace_event_file *file)
{
+ unsigned int vals_max = TRACING_MAP_FIELDS_MAX - TRACING_MAP_KEYS_MAX;
+ char *fields_str, *field_str;
+ unsigned int i, j;
int ret;
ret = create_hitcount_val(hist_data);
+ if (ret)
+ goto out;
+ fields_str = hist_data->attrs->vals_str;
+ if (!fields_str)
+ goto out;
+
+ strsep(&fields_str, "=");
+ if (!fields_str)
+ goto out;
+
+ vals_max = TRACING_MAP_FIELDS_MAX - TRACING_MAP_KEYS_MAX;
+
+ for (i = 0, j = 1; i < vals_max; i++) {
+ field_str = strsep(&fields_str, ",");
+ if (!field_str)
+ break;
+ if (!strcmp(field_str, "hitcount"))
+ continue;
+ ret = create_val_field(hist_data, j++, file, field_str);
+ if (ret)
+ goto out;
+ }
+ if (fields_str)
+ ret = -EINVAL;
+ out:
return ret;
}
@@ -542,6 +601,12 @@ hist_trigger_entry_print(struct seq_file *m,
seq_printf(m, " hitcount: %10llu",
tracing_map_read_sum(elt, HITCOUNT_IDX));
+ for (i = 1; i < hist_data->n_vals; i++) {
+ seq_printf(m, " %s: %10llu",
+ hist_data->fields[i]->field->name,
+ tracing_map_read_sum(elt, i));
+ }
+
seq_puts(m, "\n");
}
@@ -649,7 +714,15 @@ static int event_hist_trigger_print(struct seq_file *m,
}
seq_puts(m, ":vals=");
- seq_puts(m, "hitcount");
+
+ for_each_hist_val_field(i, hist_data) {
+ if (i == HITCOUNT_IDX)
+ seq_puts(m, "hitcount");
+ else {
+ seq_puts(m, ",");
+ hist_field_print(m, hist_data->fields[i]);
+ }
+ }
seq_puts(m, ":sort=");
seq_puts(m, "hitcount");
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:10 +0100 |
| Subject | [PATCH v12 08/30] tracing: Add an unreg_all() callback to trigger commands |
| Message-ID | <qy5B0-6Cy-23@gated-at.bofh.it> |
| In reply to | #1275802 |
Add a new unreg_all() callback that can be used to remove all
command-specific triggers from an event and arrange to have it called
whenever a trigger file is opened with O_TRUNC set.
Commands that don't want truncate semantics, or existing commands that
don't implement this function simply do nothing and their triggers
remain intact.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.h | 9 +++++++--
kernel/trace/trace_events_trigger.c | 13 +++++++++++++
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 300add5..2028e98 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1299,8 +1299,8 @@ struct event_trigger_ops {
* it (filters make a trigger require access to the trace record
* but are not always present).
*
- * All the methods below, except for @set_filter(), must be
- * implemented.
+ * All the methods below, except for @set_filter() and @unreg_all(),
+ * must be implemented.
*
* @func: The callback function responsible for parsing and
* registering the trigger written to the 'trigger' file by the
@@ -1325,6 +1325,10 @@ struct event_trigger_ops {
* This is usually implemented by the generic utility function
* @unregister_trigger() (see trace_event_triggers.c).
*
+ * @unreg_all: An optional function called to remove all the triggers
+ * from the list of triggers associated with the event. Called
+ * when a trigger file is opened in truncate mode.
+ *
* @set_filter: An optional function called to parse and set a filter
* for the trigger. If no @set_filter() method is set for the
* event command, filters set by the user for the command will be
@@ -1351,6 +1355,7 @@ struct event_command {
struct event_trigger_ops *ops,
struct event_trigger_data *data,
struct trace_event_file *file);
+ void (*unreg_all)(struct trace_event_file *file);
int (*set_filter)(char *filter_str,
struct event_trigger_data *data,
struct trace_event_file *file);
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 2f881e9..362ae86 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -193,6 +193,19 @@ static int event_trigger_regex_open(struct inode *inode, struct file *file)
return -ENODEV;
}
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_flags & O_TRUNC)) {
+ struct trace_event_file *event_file;
+ struct event_command *p;
+
+ event_file = event_file_data(file);
+
+ list_for_each_entry(p, &trigger_commands, list) {
+ if (p->unreg_all)
+ p->unreg_all(event_file);
+ }
+ }
+
if (file->f_mode & FMODE_READ) {
ret = seq_open(file, &event_triggers_seq_ops);
if (!ret) {
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:10 +0100 |
| Subject | [PATCH v12 06/30] tracing: Add a per-event-trigger 'paused' field |
| Message-ID | <qy5B0-6Cy-37@gated-at.bofh.it> |
| In reply to | #1275802 |
Add a simple per-trigger 'paused' flag, allowing individual triggers
to pause. We could leave it to individual triggers that need this
functionality to do it themselves, but we also want to allow other
events to control pausing, so add it to the trigger data.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
kernel/trace/trace.h | 1 +
kernel/trace/trace_events_trigger.c | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 5d5fede..5e23a97 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1173,6 +1173,7 @@ struct event_trigger_data {
struct event_filter __rcu *filter;
char *filter_str;
void *private_data;
+ bool paused;
struct list_head list;
};
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index c3abef6..8375d29 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -72,6 +72,8 @@ event_triggers_call(struct trace_event_file *file, void *rec)
return tt;
list_for_each_entry_rcu(data, &file->triggers, list) {
+ if (data->paused)
+ continue;
if (!rec) {
data->ops->func(data, rec);
continue;
@@ -110,6 +112,8 @@ event_triggers_post_call(struct trace_event_file *file,
struct event_trigger_data *data;
list_for_each_entry_rcu(data, &file->triggers, list) {
+ if (data->paused)
+ continue;
if (data->cmd_ops->trigger_type & tt)
data->ops->func(data, rec);
}
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:10 +0100 |
| Subject | [PATCH v12 03/30] tracing: Make event trigger functions available |
| Message-ID | <qy5B1-6Cy-39@gated-at.bofh.it> |
| In reply to | #1275802 |
Make various event trigger utility functions available outside of
trace_events_trigger.c so that new triggers can be defined outside of
that file.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
kernel/trace/trace.h | 14 ++++++++++++++
kernel/trace/trace_events_trigger.c | 28 +++++++++++++---------------
2 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index fd22cd4..2aa03be 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1176,6 +1176,20 @@ struct event_trigger_data {
struct list_head list;
};
+extern void trigger_data_free(struct event_trigger_data *data);
+extern int event_trigger_init(struct event_trigger_ops *ops,
+ struct event_trigger_data *data);
+extern int trace_event_trigger_enable_disable(struct trace_event_file *file,
+ int trigger_enable);
+extern void update_cond_flag(struct trace_event_file *file);
+extern void unregister_trigger(char *glob, struct event_trigger_ops *ops,
+ struct event_trigger_data *test,
+ struct trace_event_file *file);
+extern int set_trigger_filter(char *filter_str,
+ struct event_trigger_data *trigger_data,
+ struct trace_event_file *file);
+extern int register_event_command(struct event_command *cmd);
+
/**
* struct event_trigger_ops - callbacks for trace event triggers
*
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 4d2f3cc..6087052 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -28,8 +28,7 @@
static LIST_HEAD(trigger_commands);
static DEFINE_MUTEX(trigger_cmd_mutex);
-static void
-trigger_data_free(struct event_trigger_data *data)
+void trigger_data_free(struct event_trigger_data *data)
{
if (data->cmd_ops->set_filter)
data->cmd_ops->set_filter(NULL, data, NULL);
@@ -311,7 +310,7 @@ const struct file_operations event_trigger_fops = {
* Currently we only register event commands from __init, so mark this
* __init too.
*/
-static __init int register_event_command(struct event_command *cmd)
+__init int register_event_command(struct event_command *cmd)
{
struct event_command *p;
int ret = 0;
@@ -400,9 +399,8 @@ event_trigger_print(const char *name, struct seq_file *m,
*
* Return: 0 on success, errno otherwise
*/
-static int
-event_trigger_init(struct event_trigger_ops *ops,
- struct event_trigger_data *data)
+int event_trigger_init(struct event_trigger_ops *ops,
+ struct event_trigger_data *data)
{
data->ref++;
return 0;
@@ -430,8 +428,8 @@ event_trigger_free(struct event_trigger_ops *ops,
trigger_data_free(data);
}
-static int trace_event_trigger_enable_disable(struct trace_event_file *file,
- int trigger_enable)
+int trace_event_trigger_enable_disable(struct trace_event_file *file,
+ int trigger_enable)
{
int ret = 0;
@@ -488,7 +486,7 @@ clear_event_triggers(struct trace_array *tr)
* its TRIGGER_COND bit set, otherwise the TRIGGER_COND bit should be
* cleared.
*/
-static void update_cond_flag(struct trace_event_file *file)
+void update_cond_flag(struct trace_event_file *file)
{
struct event_trigger_data *data;
bool set_cond = false;
@@ -565,9 +563,9 @@ out:
* Usually used directly as the @unreg method in event command
* implementations.
*/
-static void unregister_trigger(char *glob, struct event_trigger_ops *ops,
- struct event_trigger_data *test,
- struct trace_event_file *file)
+void unregister_trigger(char *glob, struct event_trigger_ops *ops,
+ struct event_trigger_data *test,
+ struct trace_event_file *file)
{
struct event_trigger_data *data;
bool unregistered = false;
@@ -701,9 +699,9 @@ event_trigger_callback(struct event_command *cmd_ops,
*
* Return: 0 on success, errno otherwise
*/
-static int set_trigger_filter(char *filter_str,
- struct event_trigger_data *trigger_data,
- struct trace_event_file *file)
+int set_trigger_filter(char *filter_str,
+ struct event_trigger_data *trigger_data,
+ struct trace_event_file *file)
{
struct event_trigger_data *data = trigger_data;
struct event_filter *filter = NULL, *tmp;
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:10 +0100 |
| Subject | [PATCH v12 01/30] tracing: Update cond flag when enabling or disabling a trigger |
| Message-ID | <qy5B1-6Cy-41@gated-at.bofh.it> |
| In reply to | #1275802 |
When a trigger is enabled, the cond flag should be set beforehand,
otherwise a trigger that's expecting to process a trace record
(e.g. one with post_trigger set) could be invoked without one.
Likewise a trigger's cond flag should be reset after it's disabled,
not before.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de>
Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
kernel/trace/trace_events_trigger.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 42a4009..4d2f3cc 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -543,11 +543,12 @@ static int register_trigger(char *glob, struct event_trigger_ops *ops,
list_add_rcu(&data->list, &file->triggers);
ret++;
+ update_cond_flag(file);
if (trace_event_trigger_enable_disable(file, 1) < 0) {
list_del_rcu(&data->list);
+ update_cond_flag(file);
ret--;
}
- update_cond_flag(file);
out:
return ret;
}
@@ -575,8 +576,8 @@ static void unregister_trigger(char *glob, struct event_trigger_ops *ops,
if (data->cmd_ops->trigger_type == test->cmd_ops->trigger_type) {
unregistered = true;
list_del_rcu(&data->list);
- update_cond_flag(file);
trace_event_trigger_enable_disable(file, 0);
+ update_cond_flag(file);
break;
}
}
@@ -1319,11 +1320,12 @@ static int event_enable_register_trigger(char *glob,
list_add_rcu(&data->list, &file->triggers);
ret++;
+ update_cond_flag(file);
if (trace_event_trigger_enable_disable(file, 1) < 0) {
list_del_rcu(&data->list);
+ update_cond_flag(file);
ret--;
}
- update_cond_flag(file);
out:
return ret;
}
@@ -1344,8 +1346,8 @@ static void event_enable_unregister_trigger(char *glob,
(enable_data->file == test_enable_data->file)) {
unregistered = true;
list_del_rcu(&data->list);
- update_cond_flag(file);
trace_event_trigger_enable_disable(file, 0);
+ update_cond_flag(file);
break;
}
}
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Steven Rostedt <rostedt@goodmis.org> |
|---|---|
| Date | 2015-11-23 22:30 +0100 |
| Subject | Re: [PATCH v12 01/30] tracing: Update cond flag when enabling or disabling a trigger |
| Message-ID | <qy6Qq-7lC-9@gated-at.bofh.it> |
| In reply to | #1275822 |
On Mon, 23 Nov 2015 13:51:16 -0600 Tom Zanussi <tom.zanussi@linux.intel.com> wrote: > When a trigger is enabled, the cond flag should be set beforehand, > otherwise a trigger that's expecting to process a trace record > (e.g. one with post_trigger set) could be invoked without one. > > Likewise a trigger's cond flag should be reset after it's disabled, > not before. > > Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> > Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de> > Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> > Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> > --- FYI, I've applied this patch as it looks like something that should be added regardless of the rest of the series. -- Steve -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2015-11-25 02:30 +0100 |
| Subject | Re: [PATCH v12 01/30] tracing: Update cond flag when enabling or disabling a trigger |
| Message-ID | <qyx4e-7Bk-1@gated-at.bofh.it> |
| In reply to | #1275864 |
Hi Steve and Tom, On Mon, Nov 23, 2015 at 04:29:43PM -0500, Steven Rostedt wrote: > On Mon, 23 Nov 2015 13:51:16 -0600 > Tom Zanussi <tom.zanussi@linux.intel.com> wrote: > > > When a trigger is enabled, the cond flag should be set beforehand, > > otherwise a trigger that's expecting to process a trace record > > (e.g. one with post_trigger set) could be invoked without one. > > > > Likewise a trigger's cond flag should be reset after it's disabled, > > not before. > > > > Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> > > Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de> > > Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> > > Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> > > --- > > FYI, I've applied this patch as it looks like something that should be > added regardless of the rest of the series. You can add my Reviewed-by if you want. I'll try to review others soon. Thanks, Namhyung -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-11-23 21:10 +0100 |
| Subject | [PATCH v12 10/30] tracing: Add 'hist' event trigger command |
| Message-ID | <qy5B1-6Cy-43@gated-at.bofh.it> |
| In reply to | #1275802 |
'hist' triggers allow users to continually aggregate trace events,
which can then be viewed afterwards by simply reading a 'hist' file
containing the aggregation in a human-readable format.
The basic idea is very simple and boils down to a mechanism whereby
trace events, rather than being exhaustively dumped in raw form and
viewed directly, are automatically 'compressed' into meaningful tables
completely defined by the user.
This is done strictly via single-line command-line commands and
without the aid of any kind of programming language or interpreter.
A surprising number of typical use cases can be accomplished by users
via this simple mechanism. In fact, a large number of the tasks that
users typically do using the more complicated script-based tracing
tools, at least during the initial stages of an investigation, can be
accomplished by simply specifying a set of keys and values to be used
in the creation of a hash table.
The Linux kernel trace event subsystem happens to provide an extensive
list of keys and values ready-made for such a purpose in the form of
the event format files associated with each trace event. By simply
consulting the format file for field names of interest and by plugging
them into the hist trigger command, users can create an endless number
of useful aggregations to help with investigating various properties
of the system. See Documentation/trace/events.txt for examples.
hist triggers are implemented on top of the existing event trigger
infrastructure, and as such are consistent with the existing triggers
from a user's perspective as well.
The basic syntax follows the existing trigger syntax. Users start an
aggregation by writing a 'hist' trigger to the event of interest's
trigger file:
# echo hist:keys=xxx [ if filter] > event/trigger
Once a hist trigger has been set up, by default it continually
aggregates every matching event into a hash table using the event key
and a value field named 'hitcount'.
To view the aggregation at any point in time, simply read the 'hist'
file in the same directory as the 'trigger' file:
# cat event/hist
The detailed syntax provides additional options for user control, and
is described exhaustively in Documentation/trace/events.txt and in the
virtual tracing/README file in the tracing subsystem.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
include/linux/trace_events.h | 1 +
kernel/trace/Kconfig | 14 +
kernel/trace/Makefile | 1 +
kernel/trace/trace.c | 17 +
kernel/trace/trace.h | 7 +
kernel/trace/trace_events.c | 4 +
kernel/trace/trace_events_hist.c | 839 ++++++++++++++++++++++++++++++++++++
kernel/trace/trace_events_trigger.c | 1 +
8 files changed, 884 insertions(+)
create mode 100644 kernel/trace/trace_events_hist.c
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 95e8fef..e61816d 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -417,6 +417,7 @@ enum event_trigger_type {
ETT_SNAPSHOT = (1 << 1),
ETT_STACKTRACE = (1 << 2),
ETT_EVENT_ENABLE = (1 << 3),
+ ETT_EVENT_HIST = (1 << 4),
};
extern int filter_match_preds(struct event_filter *filter, void *rec);
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 0c61e98..fecfef7 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -539,6 +539,20 @@ config TRACING_MAP
generally used outside of that context, and is normally
selected by tracers that use it.
+config HIST_TRIGGERS
+ bool "Histogram triggers"
+ depends on ARCH_HAVE_NMI_SAFE_CMPXCHG
+ select TRACING_MAP
+ default n
+ help
+ Hist triggers allow one or more arbitrary trace event fields
+ to be aggregated into hash tables and dumped to stdout by
+ reading a debugfs/tracefs file. They're useful for
+ gathering quick and dirty (though precise) summaries of
+ event activity as an initial guide for further investigation
+ using more advanced tools.
+
+ See Documentation/trace/events.txt.
If in doubt, say N.
config MMIOTRACE_TEST
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 4255c40..979e7bf 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o
endif
obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o
+obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o
obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o
obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o
obj-$(CONFIG_TRACEPOINTS) += power-traces.o
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 87fb980..078db8f 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3804,6 +3804,9 @@ static const char readme_msg[] =
#ifdef CONFIG_TRACER_SNAPSHOT
"\t\t snapshot\n"
#endif
+#ifdef CONFIG_HIST_TRIGGERS
+ "\t\t hist (see below)\n"
+#endif
"\t example: echo traceoff > events/block/block_unplug/trigger\n"
"\t echo traceoff:3 > events/block/block_unplug/trigger\n"
"\t echo 'enable_event:kmem:kmalloc:3 if nr_rq > 1' > \\\n"
@@ -3819,6 +3822,20 @@ static const char readme_msg[] =
"\t To remove a trigger with a count:\n"
"\t echo '!<trigger>:0 > <system>/<event>/trigger\n"
"\t Filters can be ignored when removing a trigger.\n"
+#ifdef CONFIG_HIST_TRIGGERS
+ " hist trigger\t- If set, event hits are aggregated into a hash table\n"
+ "\t Format: hist:keys=<field1>\n"
+ "\t [:size=#entries]\n"
+ "\t [if <filter>]\n\n"
+ "\t When a matching event is hit, an entry is added to a hash\n"
+ "\t table using the key named, and the value of a sum called\n"
+ "\t 'hitcount' is incremented. Keys correspond to fields in the\n"
+ "\t event's format description. Keys can be any field. The\n"
+ "\t 'size' parameter can be used to specify more or fewer than\n"
+ "\t the default 2048 entries for the hashtable size.\n\n"
+ "\t Reading the 'hist' file for the event will dump the hash\n"
+ "\t table in its entirety to stdout."
+#endif
;
static ssize_t
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 2028e98..a2a6793 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1161,6 +1161,13 @@ extern struct mutex event_mutex;
extern struct list_head ftrace_events;
extern const struct file_operations event_trigger_fops;
+extern const struct file_operations event_hist_fops;
+
+#ifdef CONFIG_HIST_TRIGGERS
+extern int register_trigger_hist_cmd(void);
+#else
+static inline int register_trigger_hist_cmd(void) { return 0; }
+#endif
extern int register_trigger_cmds(void);
extern void clear_event_triggers(struct trace_array *tr);
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index bee1e15..abdc63f 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -2091,6 +2091,10 @@ event_create_dir(struct dentry *parent, struct trace_event_file *file)
trace_create_file("trigger", 0644, file->dir, file,
&event_trigger_fops);
+#ifdef CONFIG_HIST_TRIGGERS
+ trace_create_file("hist", 0444, file->dir, file,
+ &event_hist_fops);
+#endif
trace_create_file("format", 0444, file->dir, call,
&ftrace_event_format_fops);
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
new file mode 100644
index 0000000..3c86ab2
--- /dev/null
+++ b/kernel/trace/trace_events_hist.c
@@ -0,0 +1,839 @@
+/*
+ * trace_events_hist - trace event hist triggers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2015 Tom Zanussi <tom.zanussi@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/stacktrace.h>
+
+#include "tracing_map.h"
+#include "trace.h"
+
+struct hist_field;
+
+typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event);
+
+struct hist_field {
+ struct ftrace_event_field *field;
+ unsigned long flags;
+ hist_field_fn_t fn;
+ unsigned int size;
+};
+
+static u64 hist_field_counter(struct hist_field *field, void *event)
+{
+ return 1;
+}
+
+static u64 hist_field_string(struct hist_field *hist_field, void *event)
+{
+ char *addr = (char *)(event + hist_field->field->offset);
+
+ return (u64)(unsigned long)addr;
+}
+
+#define DEFINE_HIST_FIELD_FN(type) \
+static u64 hist_field_##type(struct hist_field *hist_field, void *event)\
+{ \
+ type *addr = (type *)(event + hist_field->field->offset); \
+ \
+ return (u64)*addr; \
+}
+
+DEFINE_HIST_FIELD_FN(s64);
+DEFINE_HIST_FIELD_FN(u64);
+DEFINE_HIST_FIELD_FN(s32);
+DEFINE_HIST_FIELD_FN(u32);
+DEFINE_HIST_FIELD_FN(s16);
+DEFINE_HIST_FIELD_FN(u16);
+DEFINE_HIST_FIELD_FN(s8);
+DEFINE_HIST_FIELD_FN(u8);
+
+#define for_each_hist_field(i, hist_data) \
+ for (i = 0; i < hist_data->n_fields; i++)
+
+#define for_each_hist_val_field(i, hist_data) \
+ for (i = 0; i < hist_data->n_vals; i++)
+
+#define for_each_hist_key_field(i, hist_data) \
+ for (i = hist_data->n_vals; i < hist_data->n_fields; i++)
+
+#define HITCOUNT_IDX 0
+#define HIST_KEY_MAX 1
+#define HIST_KEY_SIZE_MAX MAX_FILTER_STR_VAL
+
+enum hist_field_flags {
+ HIST_FIELD_HITCOUNT = 1,
+ HIST_FIELD_KEY = 2,
+ HIST_FIELD_STRING = 4,
+};
+
+struct hist_trigger_attrs {
+ char *keys_str;
+ unsigned int map_bits;
+};
+
+struct hist_trigger_data {
+ struct hist_field *fields[TRACING_MAP_FIELDS_MAX];
+ unsigned int n_vals;
+ unsigned int n_keys;
+ unsigned int n_fields;
+ unsigned int key_size;
+ struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX];
+ unsigned int n_sort_keys;
+ struct trace_event_file *event_file;
+ struct hist_trigger_attrs *attrs;
+ struct tracing_map *map;
+};
+
+static hist_field_fn_t select_value_fn(int field_size, int field_is_signed)
+{
+ hist_field_fn_t fn = NULL;
+
+ switch (field_size) {
+ case 8:
+ if (field_is_signed)
+ fn = hist_field_s64;
+ else
+ fn = hist_field_u64;
+ break;
+ case 4:
+ if (field_is_signed)
+ fn = hist_field_s32;
+ else
+ fn = hist_field_u32;
+ break;
+ case 2:
+ if (field_is_signed)
+ fn = hist_field_s16;
+ else
+ fn = hist_field_u16;
+ break;
+ case 1:
+ if (field_is_signed)
+ fn = hist_field_s8;
+ else
+ fn = hist_field_u8;
+ break;
+ }
+
+ return fn;
+}
+
+static int parse_map_size(char *str)
+{
+ unsigned long size, map_bits;
+ int ret;
+
+ strsep(&str, "=");
+ if (!str) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = kstrtoul(str, 0, &size);
+ if (ret)
+ goto out;
+
+ map_bits = ilog2(roundup_pow_of_two(size));
+ if (map_bits < TRACING_MAP_BITS_MIN ||
+ map_bits > TRACING_MAP_BITS_MAX)
+ ret = -EINVAL;
+ else
+ ret = map_bits;
+ out:
+ return ret;
+}
+
+static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
+{
+ if (!attrs)
+ return;
+
+ kfree(attrs->keys_str);
+ kfree(attrs);
+}
+
+static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
+{
+ struct hist_trigger_attrs *attrs;
+ int ret = 0;
+
+ attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
+ if (!attrs)
+ return ERR_PTR(-ENOMEM);
+
+ while (trigger_str) {
+ char *str = strsep(&trigger_str, ":");
+
+ if (!strncmp(str, "keys", strlen("keys")) ||
+ !strncmp(str, "key", strlen("key")))
+ attrs->keys_str = kstrdup(str, GFP_KERNEL);
+ else if (!strncmp(str, "size", strlen("size"))) {
+ int map_bits = parse_map_size(str);
+
+ if (map_bits < 0) {
+ ret = map_bits;
+ goto free;
+ }
+ attrs->map_bits = map_bits;
+ } else {
+ ret = -EINVAL;
+ goto free;
+ }
+ }
+
+ if (!attrs->keys_str) {
+ ret = -EINVAL;
+ goto free;
+ }
+
+ return attrs;
+ free:
+ destroy_hist_trigger_attrs(attrs);
+
+ return ERR_PTR(ret);
+}
+
+static void destroy_hist_field(struct hist_field *hist_field)
+{
+ kfree(hist_field);
+}
+
+static struct hist_field *create_hist_field(struct ftrace_event_field *field,
+ unsigned long flags)
+{
+ struct hist_field *hist_field;
+
+ if (field && is_function_field(field))
+ return NULL;
+
+ hist_field = kzalloc(sizeof(struct hist_field), GFP_KERNEL);
+ if (!hist_field)
+ return NULL;
+
+ if (flags & HIST_FIELD_HITCOUNT) {
+ hist_field->fn = hist_field_counter;
+ goto out;
+ }
+
+ if (is_string_field(field)) {
+ flags |= HIST_FIELD_STRING;
+ hist_field->fn = hist_field_string;
+ } else {
+ hist_field->fn = select_value_fn(field->size,
+ field->is_signed);
+ if (!hist_field->fn) {
+ destroy_hist_field(hist_field);
+ return NULL;
+ }
+ }
+ out:
+ hist_field->field = field;
+ hist_field->flags = flags;
+
+ return hist_field;
+}
+
+static void destroy_hist_fields(struct hist_trigger_data *hist_data)
+{
+ unsigned int i;
+
+ for_each_hist_field(i, hist_data) {
+ destroy_hist_field(hist_data->fields[i]);
+ hist_data->fields[i] = NULL;
+ }
+}
+
+static int create_hitcount_val(struct hist_trigger_data *hist_data)
+{
+ hist_data->fields[HITCOUNT_IDX] =
+ create_hist_field(NULL, HIST_FIELD_HITCOUNT);
+ if (!hist_data->fields[HITCOUNT_IDX])
+ return -ENOMEM;
+
+ hist_data->n_vals++;
+
+ return 0;
+}
+
+static int create_val_fields(struct hist_trigger_data *hist_data,
+ struct trace_event_file *file)
+{
+ int ret;
+
+ ret = create_hitcount_val(hist_data);
+
+ return ret;
+}
+
+static int create_key_field(struct hist_trigger_data *hist_data,
+ unsigned int key_idx,
+ struct trace_event_file *file,
+ char *field_str)
+{
+ struct ftrace_event_field *field = NULL;
+ unsigned long flags = 0;
+ unsigned int key_size;
+ int ret = 0;
+
+ flags |= HIST_FIELD_KEY;
+
+ field = trace_find_event_field(file->event_call, field_str);
+ if (!field) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ key_size = field->size;
+
+ hist_data->fields[key_idx] = create_hist_field(field, flags);
+ if (!hist_data->fields[key_idx]) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ key_size = ALIGN(key_size, sizeof(u64));
+ hist_data->fields[key_idx]->size = key_size;
+ hist_data->key_size = key_size;
+ if (hist_data->key_size > HIST_KEY_SIZE_MAX) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ hist_data->n_keys++;
+ ret = key_size;
+ out:
+ return ret;
+}
+
+static int create_key_fields(struct hist_trigger_data *hist_data,
+ struct trace_event_file *file)
+{
+ unsigned int i, n_vals = hist_data->n_vals;
+ char *fields_str, *field_str;
+ int ret = -EINVAL;
+
+ fields_str = hist_data->attrs->keys_str;
+ if (!fields_str)
+ goto out;
+
+ strsep(&fields_str, "=");
+ if (!fields_str)
+ goto out;
+
+ for (i = n_vals; i < n_vals + HIST_KEY_MAX; i++) {
+ field_str = strsep(&fields_str, ",");
+ if (!field_str)
+ break;
+ ret = create_key_field(hist_data, i, file, field_str);
+ if (ret < 0)
+ goto out;
+ }
+ if (fields_str) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = 0;
+ out:
+ return ret;
+}
+
+static int create_hist_fields(struct hist_trigger_data *hist_data,
+ struct trace_event_file *file)
+{
+ int ret;
+
+ ret = create_val_fields(hist_data, file);
+ if (ret)
+ goto out;
+
+ ret = create_key_fields(hist_data, file);
+ if (ret)
+ goto out;
+
+ hist_data->n_fields = hist_data->n_vals + hist_data->n_keys;
+ out:
+ return ret;
+}
+
+static int create_sort_keys(struct hist_trigger_data *hist_data)
+{
+ int ret = 0;
+
+ hist_data->n_sort_keys = 1; /* sort_keys[0] is always hitcount */
+
+ return ret;
+}
+
+static void destroy_hist_data(struct hist_trigger_data *hist_data)
+{
+ destroy_hist_trigger_attrs(hist_data->attrs);
+ destroy_hist_fields(hist_data);
+ tracing_map_destroy(hist_data->map);
+ kfree(hist_data);
+}
+
+static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
+{
+ struct tracing_map *map = hist_data->map;
+ struct ftrace_event_field *field;
+ struct hist_field *hist_field;
+ unsigned int i, idx;
+
+ for_each_hist_field(i, hist_data) {
+ hist_field = hist_data->fields[i];
+ if (hist_field->flags & HIST_FIELD_KEY) {
+ tracing_map_cmp_fn_t cmp_fn;
+
+ field = hist_field->field;
+
+ if (is_string_field(field))
+ cmp_fn = tracing_map_cmp_string;
+ else
+ cmp_fn = tracing_map_cmp_num(field->size,
+ field->is_signed);
+ idx = tracing_map_add_key_field(map, 0, cmp_fn);
+ } else
+ idx = tracing_map_add_sum_field(map);
+
+ if (idx < 0)
+ return idx;
+ }
+
+ return 0;
+}
+
+static struct hist_trigger_data *
+create_hist_data(unsigned int map_bits,
+ struct hist_trigger_attrs *attrs,
+ struct trace_event_file *file)
+{
+ struct hist_trigger_data *hist_data;
+ int ret = 0;
+
+ hist_data = kzalloc(sizeof(*hist_data), GFP_KERNEL);
+ if (!hist_data)
+ return ERR_PTR(-ENOMEM);
+
+ hist_data->attrs = attrs;
+
+ ret = create_hist_fields(hist_data, file);
+ if (ret < 0)
+ goto free;
+
+ ret = create_sort_keys(hist_data);
+ if (ret < 0)
+ goto free;
+
+ hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
+ NULL, hist_data);
+ if (IS_ERR(hist_data->map)) {
+ ret = PTR_ERR(hist_data->map);
+ hist_data->map = NULL;
+ goto free;
+ }
+
+ ret = create_tracing_map_fields(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;
+ free:
+ hist_data->attrs = NULL;
+
+ destroy_hist_data(hist_data);
+ if (ret)
+ hist_data = ERR_PTR(ret);
+ else
+ hist_data = NULL;
+
+ goto out;
+}
+
+static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
+ struct tracing_map_elt *elt,
+ void *rec)
+{
+ struct hist_field *hist_field;
+ unsigned int i;
+ 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);
+ tracing_map_update_sum(elt, i, hist_val);
+ }
+}
+
+static void event_hist_trigger(struct event_trigger_data *data, void *rec)
+{
+ struct hist_trigger_data *hist_data = data->private_data;
+ struct hist_field *key_field;
+ struct tracing_map_elt *elt;
+ u64 field_contents;
+ void *key = NULL;
+ unsigned int i;
+
+ for_each_hist_key_field(i, hist_data) {
+ key_field = hist_data->fields[i];
+
+ field_contents = key_field->fn(key_field, rec);
+ if (key_field->flags & HIST_FIELD_STRING)
+ key = (void *)(unsigned long)field_contents;
+ else
+ key = (void *)&field_contents;
+ }
+
+ elt = tracing_map_insert(hist_data->map, key);
+ if (elt)
+ hist_trigger_elt_update(hist_data, elt, rec);
+}
+
+static void
+hist_trigger_entry_print(struct seq_file *m,
+ struct hist_trigger_data *hist_data, void *key,
+ struct tracing_map_elt *elt)
+{
+ struct hist_field *key_field;
+ unsigned int i;
+ u64 uval;
+
+ seq_puts(m, "{ ");
+
+ for_each_hist_key_field(i, hist_data) {
+ key_field = hist_data->fields[i];
+
+ if (i > hist_data->n_vals)
+ seq_puts(m, ", ");
+
+ if (key_field->flags & HIST_FIELD_STRING) {
+ seq_printf(m, "%s: %-50s", key_field->field->name,
+ (char *)key);
+ } else {
+ uval = *(u64 *)key;
+ seq_printf(m, "%s: %10llu",
+ key_field->field->name, uval);
+ }
+ }
+
+ seq_puts(m, " }");
+
+ seq_printf(m, " hitcount: %10llu",
+ tracing_map_read_sum(elt, HITCOUNT_IDX));
+
+ seq_puts(m, "\n");
+}
+
+static int print_entries(struct seq_file *m,
+ struct hist_trigger_data *hist_data)
+{
+ struct tracing_map_sort_entry **sort_entries = NULL;
+ struct tracing_map *map = hist_data->map;
+ unsigned int i, n_entries;
+
+ n_entries = tracing_map_sort_entries(map, hist_data->sort_keys,
+ hist_data->n_sort_keys,
+ &sort_entries);
+ if (n_entries < 0)
+ return n_entries;
+
+ for (i = 0; i < n_entries; i++)
+ hist_trigger_entry_print(m, hist_data,
+ sort_entries[i]->key,
+ sort_entries[i]->elt);
+
+ tracing_map_destroy_sort_entries(sort_entries, n_entries);
+
+ return n_entries;
+}
+
+static int hist_show(struct seq_file *m, void *v)
+{
+ struct event_trigger_data *test, *data = NULL;
+ struct trace_event_file *event_file;
+ struct hist_trigger_data *hist_data;
+ int n_entries, ret = 0;
+
+ mutex_lock(&event_mutex);
+
+ event_file = event_file_data(m->private);
+ if (unlikely(!event_file)) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ list_for_each_entry_rcu(test, &event_file->triggers, list) {
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+ data = test;
+ break;
+ }
+ }
+ if (!data)
+ goto out_unlock;
+
+ seq_puts(m, "# event histogram\n#\n# trigger info: ");
+ data->ops->print(m, data->ops, data);
+ seq_puts(m, "\n");
+
+ hist_data = data->private_data;
+ n_entries = print_entries(m, hist_data);
+ if (n_entries < 0) {
+ ret = n_entries;
+ n_entries = 0;
+ }
+
+ seq_printf(m, "\nTotals:\n Hits: %llu\n Entries: %u\n Dropped: %llu\n",
+ (u64)atomic64_read(&hist_data->map->hits),
+ n_entries, (u64)atomic64_read(&hist_data->map->drops));
+ out_unlock:
+ mutex_unlock(&event_mutex);
+
+ return ret;
+}
+
+static int event_hist_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hist_show, file);
+}
+
+const struct file_operations event_hist_fops = {
+ .open = event_hist_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
+{
+ seq_printf(m, "%s", hist_field->field->name);
+}
+
+static int event_hist_trigger_print(struct seq_file *m,
+ struct event_trigger_ops *ops,
+ struct event_trigger_data *data)
+{
+ struct hist_trigger_data *hist_data = data->private_data;
+ struct hist_field *key_field;
+ unsigned int i;
+
+ seq_puts(m, "hist:keys=");
+
+ for_each_hist_key_field(i, hist_data) {
+ key_field = hist_data->fields[i];
+
+ if (i > hist_data->n_vals)
+ seq_puts(m, ",");
+
+ hist_field_print(m, key_field);
+ }
+
+ seq_puts(m, ":vals=");
+ seq_puts(m, "hitcount");
+
+ seq_puts(m, ":sort=");
+ seq_puts(m, "hitcount");
+
+ seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits));
+
+ if (data->filter_str)
+ seq_printf(m, " if %s", data->filter_str);
+
+ seq_puts(m, " [active]");
+
+ seq_putc(m, '\n');
+
+ return 0;
+}
+
+static void event_hist_trigger_free(struct event_trigger_ops *ops,
+ struct event_trigger_data *data)
+{
+ struct hist_trigger_data *hist_data = data->private_data;
+
+ if (WARN_ON_ONCE(data->ref <= 0))
+ return;
+
+ data->ref--;
+ if (!data->ref) {
+ trigger_data_free(data);
+ destroy_hist_data(hist_data);
+ }
+}
+
+static struct event_trigger_ops event_hist_trigger_ops = {
+ .func = event_hist_trigger,
+ .print = event_hist_trigger_print,
+ .init = event_trigger_init,
+ .free = event_hist_trigger_free,
+};
+
+static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd,
+ char *param)
+{
+ return &event_hist_trigger_ops;
+}
+
+static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
+ struct event_trigger_data *data,
+ struct trace_event_file *file)
+{
+ struct event_trigger_data *test;
+ int ret = 0;
+
+ list_for_each_entry_rcu(test, &file->triggers, list) {
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+
+ if (data->ops->init) {
+ ret = data->ops->init(data->ops, data);
+ if (ret < 0)
+ goto out;
+ }
+
+ list_add_rcu(&data->list, &file->triggers);
+ ret++;
+
+ 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 int event_hist_trigger_func(struct event_command *cmd_ops,
+ struct trace_event_file *file,
+ char *glob, char *cmd, char *param)
+{
+ unsigned int hist_trigger_bits = TRACING_MAP_BITS_DEFAULT;
+ struct event_trigger_data *trigger_data;
+ struct hist_trigger_attrs *attrs;
+ struct event_trigger_ops *trigger_ops;
+ struct hist_trigger_data *hist_data;
+ char *trigger;
+ int ret = 0;
+
+ if (!param)
+ return -EINVAL;
+
+ /* separate the trigger from the filter (k:v [if filter]) */
+ trigger = strsep(¶m, " \t");
+ if (!trigger)
+ return -EINVAL;
+
+ attrs = parse_hist_trigger_attrs(trigger);
+ if (IS_ERR(attrs))
+ return PTR_ERR(attrs);
+
+ if (attrs->map_bits)
+ hist_trigger_bits = attrs->map_bits;
+
+ hist_data = create_hist_data(hist_trigger_bits, attrs, file);
+ if (IS_ERR(hist_data)) {
+ destroy_hist_trigger_attrs(attrs);
+ return PTR_ERR(hist_data);
+ }
+
+ trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger);
+
+ ret = -ENOMEM;
+ trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL);
+ if (!trigger_data)
+ goto out_free;
+
+ trigger_data->count = -1;
+ trigger_data->ops = trigger_ops;
+ trigger_data->cmd_ops = cmd_ops;
+
+ INIT_LIST_HEAD(&trigger_data->list);
+ RCU_INIT_POINTER(trigger_data->filter, NULL);
+
+ trigger_data->private_data = hist_data;
+
+ if (glob[0] == '!') {
+ cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
+ ret = 0;
+ goto out_free;
+ }
+
+ if (!param) /* if param is non-empty, it's supposed to be a filter */
+ goto out_reg;
+
+ if (!cmd_ops->set_filter)
+ goto out_reg;
+
+ ret = cmd_ops->set_filter(param, trigger_data, file);
+ if (ret < 0)
+ goto out_free;
+ out_reg:
+ 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
+ * triggers registered a failure too.
+ */
+ if (!ret) {
+ ret = -ENOENT;
+ goto out_free;
+ } else if (ret < 0)
+ goto out_free;
+ /* Just return zero, not the number of registered triggers */
+ ret = 0;
+ out:
+ return ret;
+ out_free:
+ if (cmd_ops->set_filter)
+ cmd_ops->set_filter(NULL, trigger_data, NULL);
+
+ kfree(trigger_data);
+
+ destroy_hist_data(hist_data);
+ goto out;
+}
+
+static struct event_command trigger_hist_cmd = {
+ .name = "hist",
+ .trigger_type = ETT_EVENT_HIST,
+ .needs_rec = true,
+ .func = event_hist_trigger_func,
+ .reg = hist_register_trigger,
+ .unreg = unregister_trigger,
+ .get_trigger_ops = event_hist_get_trigger_ops,
+ .set_filter = set_trigger_filter,
+};
+
+__init int register_trigger_hist_cmd(void)
+{
+ int ret;
+
+ ret = register_event_command(&trigger_hist_cmd);
+ WARN_ON(ret < 0);
+
+ return ret;
+}
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 362ae86..1f1db3c 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -1452,6 +1452,7 @@ __init int register_trigger_cmds(void)
register_trigger_snapshot_cmd();
register_trigger_stacktrace_cmd();
register_trigger_enable_disable_cmds();
+ register_trigger_hist_cmd();
return 0;
}
--
1.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2015-11-29 16:00 +0100 |
| Subject | Re: [PATCH v12 10/30] tracing: Add 'hist' event trigger command |
| Message-ID | <qAbCh-7py-7@gated-at.bofh.it> |
| In reply to | #1275823 |
Hi Tom,
On Mon, Nov 23, 2015 at 01:51:25PM -0600, Tom Zanussi wrote:
> 'hist' triggers allow users to continually aggregate trace events,
> which can then be viewed afterwards by simply reading a 'hist' file
> containing the aggregation in a human-readable format.
>
> The basic idea is very simple and boils down to a mechanism whereby
> trace events, rather than being exhaustively dumped in raw form and
> viewed directly, are automatically 'compressed' into meaningful tables
> completely defined by the user.
>
> This is done strictly via single-line command-line commands and
> without the aid of any kind of programming language or interpreter.
>
> A surprising number of typical use cases can be accomplished by users
> via this simple mechanism. In fact, a large number of the tasks that
> users typically do using the more complicated script-based tracing
> tools, at least during the initial stages of an investigation, can be
> accomplished by simply specifying a set of keys and values to be used
> in the creation of a hash table.
>
> The Linux kernel trace event subsystem happens to provide an extensive
> list of keys and values ready-made for such a purpose in the form of
> the event format files associated with each trace event. By simply
> consulting the format file for field names of interest and by plugging
> them into the hist trigger command, users can create an endless number
> of useful aggregations to help with investigating various properties
> of the system. See Documentation/trace/events.txt for examples.
>
> hist triggers are implemented on top of the existing event trigger
> infrastructure, and as such are consistent with the existing triggers
> from a user's perspective as well.
>
> The basic syntax follows the existing trigger syntax. Users start an
> aggregation by writing a 'hist' trigger to the event of interest's
> trigger file:
>
> # echo hist:keys=xxx [ if filter] > event/trigger
>
> Once a hist trigger has been set up, by default it continually
> aggregates every matching event into a hash table using the event key
> and a value field named 'hitcount'.
>
> To view the aggregation at any point in time, simply read the 'hist'
> file in the same directory as the 'trigger' file:
>
> # cat event/hist
>
> The detailed syntax provides additional options for user control, and
> is described exhaustively in Documentation/trace/events.txt and in the
> virtual tracing/README file in the tracing subsystem.
>
> Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
> Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
> ---
[SNIP]
> +static struct hist_trigger_data *
> +create_hist_data(unsigned int map_bits,
> + struct hist_trigger_attrs *attrs,
> + struct trace_event_file *file)
> +{
> + struct hist_trigger_data *hist_data;
> + int ret = 0;
> +
> + hist_data = kzalloc(sizeof(*hist_data), GFP_KERNEL);
> + if (!hist_data)
> + return ERR_PTR(-ENOMEM);
> +
> + hist_data->attrs = attrs;
> +
> + ret = create_hist_fields(hist_data, file);
> + if (ret < 0)
> + goto free;
> +
> + ret = create_sort_keys(hist_data);
> + if (ret < 0)
> + goto free;
> +
> + hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
> + NULL, hist_data);
> + if (IS_ERR(hist_data->map)) {
> + ret = PTR_ERR(hist_data->map);
> + hist_data->map = NULL;
> + goto free;
> + }
> +
> + ret = create_tracing_map_fields(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;
> + free:
> + hist_data->attrs = NULL;
> +
> + destroy_hist_data(hist_data);
> + if (ret)
> + hist_data = ERR_PTR(ret);
> + else
> + hist_data = NULL;
Is this else part really needed? It seems that all code path to here
set the 'ret' non-zero. Otherwise ...
> +
> + goto out;
> +}
[SNIP]
> +static int event_hist_trigger_func(struct event_command *cmd_ops,
> + struct trace_event_file *file,
> + char *glob, char *cmd, char *param)
> +{
> + unsigned int hist_trigger_bits = TRACING_MAP_BITS_DEFAULT;
> + struct event_trigger_data *trigger_data;
> + struct hist_trigger_attrs *attrs;
> + struct event_trigger_ops *trigger_ops;
> + struct hist_trigger_data *hist_data;
> + char *trigger;
> + int ret = 0;
> +
> + if (!param)
> + return -EINVAL;
> +
> + /* separate the trigger from the filter (k:v [if filter]) */
> + trigger = strsep(¶m, " \t");
> + if (!trigger)
> + return -EINVAL;
> +
> + attrs = parse_hist_trigger_attrs(trigger);
> + if (IS_ERR(attrs))
> + return PTR_ERR(attrs);
> +
> + if (attrs->map_bits)
> + hist_trigger_bits = attrs->map_bits;
> +
> + hist_data = create_hist_data(hist_trigger_bits, attrs, file);
> + if (IS_ERR(hist_data)) {
... it should be IS_ERR_OR_NULL(hist_data) then.
Thanks,
Namhyung
> + destroy_hist_trigger_attrs(attrs);
> + return PTR_ERR(hist_data);
> + }
> +
> + trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger);
> +
> + ret = -ENOMEM;
> + trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL);
> + if (!trigger_data)
> + goto out_free;
> +
> + trigger_data->count = -1;
> + trigger_data->ops = trigger_ops;
> + trigger_data->cmd_ops = cmd_ops;
> +
> + INIT_LIST_HEAD(&trigger_data->list);
> + RCU_INIT_POINTER(trigger_data->filter, NULL);
> +
> + trigger_data->private_data = hist_data;
> +
> + if (glob[0] == '!') {
> + cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
> + ret = 0;
> + goto out_free;
> + }
> +
> + if (!param) /* if param is non-empty, it's supposed to be a filter */
> + goto out_reg;
> +
> + if (!cmd_ops->set_filter)
> + goto out_reg;
> +
> + ret = cmd_ops->set_filter(param, trigger_data, file);
> + if (ret < 0)
> + goto out_free;
> + out_reg:
> + 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
> + * triggers registered a failure too.
> + */
> + if (!ret) {
> + ret = -ENOENT;
> + goto out_free;
> + } else if (ret < 0)
> + goto out_free;
> + /* Just return zero, not the number of registered triggers */
> + ret = 0;
> + out:
> + return ret;
> + out_free:
> + if (cmd_ops->set_filter)
> + cmd_ops->set_filter(NULL, trigger_data, NULL);
> +
> + kfree(trigger_data);
> +
> + destroy_hist_data(hist_data);
> + goto out;
> +}
> +
> +static struct event_command trigger_hist_cmd = {
> + .name = "hist",
> + .trigger_type = ETT_EVENT_HIST,
> + .needs_rec = true,
> + .func = event_hist_trigger_func,
> + .reg = hist_register_trigger,
> + .unreg = unregister_trigger,
> + .get_trigger_ops = event_hist_get_trigger_ops,
> + .set_filter = set_trigger_filter,
> +};
> +
> +__init int register_trigger_hist_cmd(void)
> +{
> + int ret;
> +
> + ret = register_event_command(&trigger_hist_cmd);
> + WARN_ON(ret < 0);
> +
> + return ret;
> +}
> diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
> index 362ae86..1f1db3c 100644
> --- a/kernel/trace/trace_events_trigger.c
> +++ b/kernel/trace/trace_events_trigger.c
> @@ -1452,6 +1452,7 @@ __init int register_trigger_cmds(void)
> register_trigger_snapshot_cmd();
> register_trigger_stacktrace_cmd();
> register_trigger_enable_disable_cmds();
> + register_trigger_hist_cmd();
>
> return 0;
> }
> --
> 1.9.3
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2015-11-29 16:30 +0100 |
| Message-ID | <qAc5k-7P9-17@gated-at.bofh.it> |
| In reply to | #1275802 |
On Mon, Nov 23, 2015 at 01:51:15PM -0600, Tom Zanussi wrote:
> This is v12 of the 'hist triggers' patchset, incorporating suggestions
> and fixing problems pointed out by Namhyung.
Thanks for doing this work! This version looks good to me although I
have a small nitpick in the 10/30. With that fixed, you can add my
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
to this patchset.
Thanks,
Namhyung
>
> Changes from v11:
>
> - Added tracing_map_lookup().
> - Changed tracing_map_insert() to incorporate the hits/drops counter
> updating. The changes allow clients to continue calling
> tracing_map_insert() and update existing entries if desired, or
> stop on the first drop, since the return value is still NULL in
> that case.
> - Changed the hist trigger code to continue calling
> tracing_map_insert() rather than stopping on the first drop. The
> idea is that there's no harm in continuing after the first drop -
> if the user can't tolerate any drops, they'll still see a non-zero
> drop count and can retry with a larger table, but if the user
> doesn't care, the hit and drop counts are an accurate reflection
> of how many events were tallied vs dropped.
> - Added a new unreg_all() callback to event_command, and code to
> call it when the trigger file is opened for truncate. Implemented
> unreg_all() for hist triggers, which allows them to honor the
> truncate flag while leaving existing triggers intact. This means
> that '>>' should now be used for adding multiple hist triggers as
> well as to clear/pause/cont an existing hist trigger.
> - Included Namhyung's log2 patch.
> - Fixed one of Masami's test cases which now requires using '>>' due
> to the truncate changes.
> - 'cont' and 'clear' no longer create a trigger if one doesn't exist.
> - Rebased to latest trace/for-next.
> - Various other smaller fixes and Documentation updates as noted by
> Namhyung.
>
> Changes from v10:
>
> - Rebased to latest trace/for-next.
> - Added Masami's ftrace/hist triggers kselftest patches.
> - Added Masami's Tested-by: to the series.
>
> Changes from v9:
>
> v10 adds some major new features - 'named' hist triggers and support
> for multiple hist triggers on a single event - basically all of the
> features and suggestions suggested by Masami, except for one: I
> decided after all not to implement the 'pause/continue/clear' commands
> on the hist file. I've gotten so used to the habit of calling up the
> previous trigger-setting command in the command history and simply
> appending a 'pause/cont/clear' command to it (and later removing it by
> prepending a '!') that making the user change the command doesn't seem
> as naturally usable. Masami, if you disagree, let me know and I'll
> reconsider it but at this point I'd rather not make that change.
>
> In addition, I think I've fixed all the other various smaller problems
> mentioned in the previous review, please let me know if I missed any...
>
> - Added support for 'named' hist triggers
> - Added support for multiple hist triggers on a single trace event
> - Added documentation and examples for the above new features
> - Streamlined the README to only include the essentials for hist triggers
> - Fixed the 'hex' modifier for values, which was previously ignored
> - Replaced the kmalloc of large arrays with a simple page-based scheme
> - Fixed the tracing_map_insert() case where jhash returns 0
> - Fixed various other small problems noted along the way
>
> Changes from v8:
>
> Same as v8, but with the RFC patch [ftrace: Add function_hist tracer]
> removed, and rebased to latest trace/for-next.
>
> Changes from v7:
>
> This version refactors the commits as suggested by Masami. There are
> now more commits, but the result should be much more reviewable. The
> ending code is the same as before, modulo a couple minor bug fixes I
> discovered while refactoring and testing.
>
> I've also reviewed and fixed a number of shortcomings and errors in
> the comments, and have added a new discussion of the tracing_map data
> structures after Steve mentioned he found them confusing and/or
> insufficiently documented.
>
> Also, I kept Namhyung's string patch [tracing: Support string type key
> properly] as submitted, but added a follow-on patch that refactors it
> and fixes a problem I found with it that enabled static string keys to
> contain random chars and therefore incorrect map insertions.
>
> Changes from v6:
>
> This version adds a new 'sym-offset' modifier as requested by Masami.
> I implemented it as a modifier rather than using the trace option as
> suggested, in part because I wanted to keep it all self-contained and
> it seemed more consistent to just add it alongside the 'sym' modifier.
> Also, hist triggers arent't really a tracer and therefore don't
> directly tie into the option update/callback mechanism so making use
> of it isn't as simple as a normal tracer.
>
> I also changed the sort key specification to be stricter and signal an
> error if the specified sort key wasn't found (rather than defaulting
> to hitcount in those cases), also suggested by Masami. Thanks,
> Masami, for your input!
>
> Also updated the Documentation and tracing/README to reflect the
> changes.
>
> Changes from v5:
>
> This version adds support for compound keys, along with the related
> ability to sort using primary and secondary keys. This was mentioned
> in previous versions as the last important piece that remained
> unimplemented, and is now implemented. (I didn't have time to get to
> the couple of enhancements suggested by Masami, but I expect to be
> able to add those later on top of these.)
>
> Because we now support compound keys and it's not immediately clear in
> the output exactly which fields correspond to keys, the key(s),
> compound or not, are now enclosed by curly braces.
>
> The Documentation and README have been updated to reflect the changes,
> and several new examples have been added to illustrate how to use
> compound keys.
>
> Also, the code was updated to work with the new ftrace_event_file,
> etc, renaming in tracing/for-next.
>
> Changes from v4:
>
> This version addresses some problems and suggestions made by Daniel
> Wagner - a lot of the code was reworked to get rid of the distinction
> between keys and values, and as a result, both keys and values can be
> used as sort keys. As suggested, it also allows 'val=' to be absent
> in a trigger command - if no 'val' is specified, hitcount is assumed
> and automatically used as the only val.
>
> The map code was also separated out into a separate file,
> tracing_map.c, allowing it to be reused. It also adds a second tracer
> called function_hist that actually does reuse the code, as an RFC
> patch.
>
> Patch 01/10 [tracing: Update cond flag when enabling or disabling..]
> is a fix for a problem noticed by Daniel and that fixes a problem in
> existing trigger code and should be applied regardless of whether the
> rest of the patchset is merged.
>
> As mentioned, patch 10/10 is an RFC patch implementing a new tracer
> based on the function tracer code. It's a fun little tool and is
> useful for a specific problem I'm working on (and is also a nice test
> of the tracing_map code), but is an RFC because first, I'm not sure it
> would really be of general interest and secondly, it's POC-level
> quality and I'd need to spend more time fixing it up to make it
> upstreamable, but I don't want to waste my time if not.
>
> There are a couple of important bits of functionality that were
> present in v1 but not yet reimplemented in v5.
>
> The first is support for compound keys. Currently, maps can only be
> keyed on a single event field, whereas in v1 they could be keyed on
> multiple keys. With support for compound keys, you can create much
> more interesting output, such as for example per-pid lists of
> syscalls or read counts e.g.:
>
> # echo 'hist:keys=common_pid.execname,id.syscall:vals=hitcount' > \
> /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/trigger
>
> # cat /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/hist
>
> key: common_pid:bash[3112], id:sys_write vals: count:69
> key: common_pid:bash[3112], id:sys_rt_sigprocmask vals: count:218
>
> key: common_pid:update-notifier[3164], id:sys_poll vals: count:37
> key: common_pid:update-notifier[3164], id:sys_recvfrom vals: count:118
>
> key: common_pid:deja-dup-monito[3194], id:sys_sendto vals: count:1
> key: common_pid:deja-dup-monito[3194], id:sys_read vals: count:4
> key: common_pid:deja-dup-monito[3194], id:sys_poll vals: count:8
> key: common_pid:deja-dup-monito[3194], id:sys_recvmsg vals: count:8
> key: common_pid:deja-dup-monito[3194], id:sys_getegid vals: count:8
>
> key: common_pid:emacs[3275], id:sys_fsync vals: count:1
> key: common_pid:emacs[3275], id:sys_open vals: count:1
> key: common_pid:emacs[3275], id:sys_symlink vals: count:2
> key: common_pid:emacs[3275], id:sys_poll vals: count:23
> key: common_pid:emacs[3275], id:sys_select vals: count:23
> key: common_pid:emacs[3275], id:unknown_syscall vals: count:34
> key: common_pid:emacs[3275], id:sys_ioctl vals: count:60
> key: common_pid:emacs[3275], id:sys_rt_sigprocmask vals: count:116
>
> key: common_pid:cat[3323], id:sys_munmap vals: count:1
> key: common_pid:cat[3323], id:sys_fadvise64 vals: count:1
>
> Related to that is support for sorting on multiple fields. Currently,
> you can sort using only a primary key. Being able to sort on multiple
> or at least a secondary key is indispensible for seeing trends when
> displaying multiple values.
>
> Changes from v3:
>
> v4 fixes the race in tracing_map_insert() noted in v3, where
> map.val.key could be checked even if map.val wasn't yet set. The
> simple fix for that in tracing_map_insert() introduces the possibility
> of duplicates in the map, which though rare, need to be accounted for
> in the output. To address that, duplicate-merging code was added to
> the map-printing code.
>
> It was also pointed out that it didn't seem correct to include
> module.h, but the fix for that has deeper roots and is being addressed
> by a separate patchset; for now we need to continue including
> module.h, though prompted by that I did some other header include
> cleanup.
>
> The functionality remains the same as v2, but this version no longer
> tries to export and use bpf_maps, and more importantly removes the
> associated GFP_NOTRACE/trace event hacks and kmem macros required to
> work around the bpf_map implementation.
>
> The tracing_map functionality is instead built on top of a simple
> lock-free map algorithm originated by Dr. Cliff Click (see references
> in the code for more details), which though too restrictive to be
> general-purpose in its current form, functions nicely as a
> special-purpose tracing map.
>
> v3 also moves the hist triggers code into a separate file and puts it
> all behind a new config option, CONFIG_HIST_TRIGGERS. It also merges
> in the sorting code rather than keeping it as a separate patch.
>
> This patchset also includes a couple other new and related triggers,
> enable_hist and disable_hist, very similar to the existing
> enable_event/disable_event triggers used to automatically enable and
> disable events based on a triggering condition, but in this case
> allowing hist triggers to be enabled and disabled in the same way.
>
> - Added an insert check for val before checking the key associated with val
> - Added code to merge possible duplicates in the map
>
> Changes from v2:
> - reimplemented tracing_map, replacing bpf_map with nmi-safe/lock-free map
> - removed GPF_NOTRACE, kmalloc/free macros and event hacks needed by bpf_maps
> - moved hist triggers from trace_events_trigger.c to trace_events_hist.c
> - added CONFIG_HIST_TRIGGERS config option
> - consolidated sorting code with main patch
>
> Changes from v1:
> - completely rewritten on top of tracing_map (renamed and exported bpf_map)
> - added map clearing and client ops to tracing_map
> - changed the name from 'hash' triggers to 'hist' triggers
> - added new trigger 'pause' feature
> - added new enable_hist and disable_hist triggers
> - added usage for hist/enable_hist/disable hist to tracing/README
> - moved examples into Documentation/trace/event.txt
> - added ___GFP_NOTRACE, kmalloc/kfree macros, and conditional kmem tracepoints
>
> The following changes since commit e428abbbf616cd8fdd1162e4a624ad1d47b47544:
>
> tracing: #ifdef out uses of max trace when CONFIG_TRACER_MAX_TRACE is not set (2015-11-10 10:16:05 -0500)
>
> are available in the git repository at:
>
> git://git.yoctoproject.org/linux-yocto-contrib.git tzanussi/hist-triggers-v12
> http://git.yoctoproject.org/cgit/cgit.cgi/linux-yocto-contrib/log/?h=tzanussi/hist-triggers-v12
>
> Masami Hiramatsu (2):
> kselftests/ftrace : Add event trigger testcases
> kselftests/ftrace: Add hist trigger testcases
>
> Namhyung Kim (2):
> tracing: Support string type key properly
> tracing: Add hist trigger 'log2' modifier
>
> Tom Zanussi (26):
> tracing: Update cond flag when enabling or disabling a trigger
> tracing: Make ftrace_event_field checking functions available
> tracing: Make event trigger functions available
> tracing: Add event record param to trigger_ops.func()
> tracing: Add get_syscall_name()
> tracing: Add a per-event-trigger 'paused' field
> tracing: Add needs_rec flag to event triggers
> tracing: Add an unreg_all() callback to trigger commands
> tracing: Add lock-free tracing_map
> tracing: Add 'hist' event trigger command
> tracing: Add hist trigger support for multiple values ('vals=' param)
> tracing: Add hist trigger support for compound keys
> tracing: Add hist trigger support for user-defined sorting ('sort='
> param)
> tracing: Add hist trigger support for pausing and continuing a trace
> tracing: Add hist trigger support for clearing a trace
> tracing: Add hist trigger 'hex' modifier for displaying numeric fields
> tracing: Add hist trigger 'sym' and 'sym-offset' modifiers
> tracing: Add hist trigger 'execname' modifier
> tracing: Add hist trigger 'syscall' modifier
> tracing: Add hist trigger support for stacktraces as keys
> tracing: Remove restriction on string position in hist trigger keys
> tracing: Add enable_hist/disable_hist triggers
> tracing: Add 'hist' trigger Documentation
> tracing: Add support for multiple hist triggers per event
> tracing: Add support for named triggers
> tracing: Add support for named hist triggers
>
> Documentation/trace/events.txt | 1555 ++++++++++++++++++
> include/linux/trace_events.h | 9 +-
> kernel/trace/Kconfig | 27 +
> kernel/trace/Makefile | 2 +
> kernel/trace/trace.c | 57 +
> kernel/trace/trace.h | 106 +-
> kernel/trace/trace_events.c | 4 +
> kernel/trace/trace_events_filter.c | 12 -
> kernel/trace/trace_events_hist.c | 1731 ++++++++++++++++++++
> kernel/trace/trace_events_trigger.c | 309 +++-
> kernel/trace/trace_syscalls.c | 11 +
> kernel/trace/tracing_map.c | 1058 ++++++++++++
> kernel/trace/tracing_map.h | 282 ++++
> tools/testing/selftests/ftrace/test.d/functions | 9 +
> .../ftrace/test.d/trigger/trigger-eventonoff.tc | 64 +
> .../ftrace/test.d/trigger/trigger-filter.tc | 59 +
> .../ftrace/test.d/trigger/trigger-hist-mod.tc | 65 +
> .../ftrace/test.d/trigger/trigger-hist.tc | 83 +
> .../ftrace/test.d/trigger/trigger-multihist.tc | 73 +
> .../ftrace/test.d/trigger/trigger-snapshot.tc | 56 +
> .../ftrace/test.d/trigger/trigger-stacktrace.tc | 53 +
> .../ftrace/test.d/trigger/trigger-traceonoff.tc | 58 +
> 22 files changed, 5596 insertions(+), 87 deletions(-)
> create mode 100644 kernel/trace/trace_events_hist.c
> create mode 100644 kernel/trace/tracing_map.c
> create mode 100644 kernel/trace/tracing_map.h
> create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc
> create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc
> create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc
> create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc
> create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc
> create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc
> create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc
> create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc
>
> --
> 1.9.3
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web