Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1186095 > unrolled thread
| Started by | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| First post | 2015-07-16 19:30 +0200 |
| Last post | 2015-07-16 23:40 +0200 |
| Articles | 20 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH v9 00/22] tracing: 'hist' triggers Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 09/22] tracing: Add hist trigger support for multiple values ('vals=' param) Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 01/22] tracing: Update cond flag when enabling or disabling a trigger Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 15/22] tracing: Add hist trigger 'sym' and 'sym-offset' modifiers Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 16/22] tracing: Add hist trigger 'execname' modifier Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 11/22] tracing: Add hist trigger support for user-defined sorting ('sort=' param) Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 12/22] tracing: Add hist trigger support for pausing and continuing a trace Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
Re: [PATCH v9 12/22] tracing: Add hist trigger support for pausing and continuing a trace Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> - 2015-07-22 10:30 +0200
[PATCH v9 08/22] tracing: Add 'hist' event trigger command Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
Re: [PATCH v9 08/22] tracing: Add 'hist' event trigger command Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> - 2015-07-20 15:40 +0200
[PATCH v9 14/22] tracing: Add hist trigger 'hex' modifier for displaying numeric fields Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 17/22] tracing: Add hist trigger 'syscall' modifier Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 04/22] tracing: Add event record param to trigger_ops.func() Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
[PATCH v9 05/22] tracing: Add get_syscall_name() Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 19:30 +0200
Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map Peter Zijlstra <peterz@infradead.org> - 2015-07-16 20:00 +0200
Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 23:50 +0200
Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map Peter Zijlstra <peterz@infradead.org> - 2015-07-17 00:40 +0200
Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-17 04:00 +0200
Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map Peter Zijlstra <peterz@infradead.org> - 2015-07-16 20:10 +0200
Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map Tom Zanussi <tom.zanussi@linux.intel.com> - 2015-07-16 23:40 +0200
| From | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-07-16 19:30 +0200 |
| Subject | [PATCH v9 00/22] tracing: 'hist' triggers |
| Message-ID | <pMV8R-4VI-3@gated-at.bofh.it> |
This is v9 of the 'hist triggers' patchset.
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 b44754d8262d3aab842998cf747f44fe6090be9f:
ring_buffer: Allow to exit the ring buffer benchmark immediately (2015-06-15 12:03:12 -0400)
are available in the git repository at:
git://git.yoctoproject.org/linux-yocto-contrib.git tzanussi/hist-triggers-v9
http://git.yoctoproject.org/cgit/cgit.cgi/linux-yocto-contrib/log/?h=tzanussi/hist-triggers-v9
Namhyung Kim (1):
tracing: Support string type key properly
Tom Zanussi (21):
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 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
Documentation/trace/events.txt | 1131 +++++++++++++++++++++++++++
include/linux/trace_events.h | 9 +-
kernel/trace/Kconfig | 14 +
kernel/trace/Makefile | 2 +
kernel/trace/trace.c | 66 ++
kernel/trace/trace.h | 77 +-
kernel/trace/trace_events.c | 4 +
kernel/trace/trace_events_filter.c | 12 -
kernel/trace/trace_events_hist.c | 1462 +++++++++++++++++++++++++++++++++++
kernel/trace/trace_events_trigger.c | 149 ++--
kernel/trace/trace_syscalls.c | 11 +
kernel/trace/tracing_map.c | 935 ++++++++++++++++++++++
kernel/trace/tracing_map.h | 258 +++++++
13 files changed, 4046 insertions(+), 84 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
--
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-07-16 19:30 +0200 |
| Subject | [PATCH v9 09/22] tracing: Add hist trigger support for multiple values ('vals=' param) |
| Message-ID | <pMV8T-4VI-41@gated-at.bofh.it> |
| In reply to | #1186095 |
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>
---
kernel/trace/trace.c | 11 ++++--
kernel/trace/trace_events_hist.c | 75 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 82 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index f6fdda2..8109b89 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3788,12 +3788,17 @@ 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. Keys correspond to fields in the\n"
- "\t event's format description. On an event hit, the value of a\n"
- "\t sum called 'hitcount' is incremented, which is simply a count\n"
+ "\t table using the key and value(s) named. Keys and values\n"
+ "\t correspond to fields in the event's format description.\n"
+ "\t Values must correspond to numeric fields - on an event hit,\n"
+ "\t the value(s) will be added to a sum kept for that field.\n"
+ "\t The special string 'hitcount' can be used in place of an\n"
+ "\t explicit value field - this is simply a count of event hits.\n"
+ "\t If 'values' is not specified, 'hitcount' will be assumed.\n"
"\t of event hits. Keys can be any field.\n\n"
"\t Reading the 'hist' file for the event will dump the hash\n"
"\t table in its entirety to stdout. Each printed hash table\n"
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index ded348b..503df07 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -75,6 +75,7 @@ enum hist_field_flags {
struct hist_trigger_attrs {
char *keys_str;
+ char *vals_str;
unsigned int map_bits;
};
@@ -155,6 +156,7 @@ static int parse_map_size(char *str)
static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
{
kfree(attrs->keys_str);
+ kfree(attrs->vals_str);
kfree(attrs);
}
@@ -173,6 +175,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);
@@ -256,13 +262,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;
}
@@ -534,6 +593,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");
}
@@ -641,7 +706,15 @@ static int event_hist_trigger_print(struct seq_file *m,
}
seq_puts(m, ":vals=");
- seq_puts(m, "hitcount");
+
+ for (i = 0; i < hist_data->n_vals; i++) {
+ if (i == 0)
+ 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-07-16 19:30 +0200 |
| Subject | [PATCH v9 01/22] tracing: Update cond flag when enabling or disabling a trigger |
| Message-ID | <pMV8T-4VI-45@gated-at.bofh.it> |
| In reply to | #1186095 |
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>
---
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 | Tom Zanussi <tom.zanussi@linux.intel.com> |
|---|---|
| Date | 2015-07-16 19:30 +0200 |
| Subject | [PATCH v9 15/22] tracing: Add hist trigger 'sym' and 'sym-offset' modifiers |
| Message-ID | <pMV8T-4VI-47@gated-at.bofh.it> |
| In reply to | #1186095 |
Allow users to have address fields displayed as symbols in the output
by appending '.sym' or 'sym-offset' to field names:
# echo hist:keys=aaa.sym,bbb.sym-offset ... \
[ if filter] > event/trigger
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.c | 2 ++
kernel/trace/trace_events_hist.c | 21 +++++++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 14f9472..8cdc7b3 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3814,6 +3814,8 @@ static const char readme_msg[] =
"\t integers. This can be modified by appending any of the\n"
"\t following modifiers to the field name:\n\n"
"\t .hex display a number as a hex value\n\n"
+ "\t .sym display an address as a symbol\n"
+ "\t .sym-offset display an address as a symbol and offset\n"
"\t By default, the size of the hash table is 2048 entries. The\n"
"\t 'size' param can be used to specify more or fewer than that.\n"
"\t The units are in terms of hashtable entries - if a run uses\n"
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 9cc38ee..106d557 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -73,6 +73,8 @@ enum hist_field_flags {
HIST_FIELD_KEY = 2,
HIST_FIELD_STRING = 4,
HIST_FIELD_HEX = 8,
+ HIST_FIELD_SYM = 16,
+ HIST_FIELD_SYM_OFFSET = 32,
};
struct hist_trigger_attrs {
@@ -370,6 +372,10 @@ static int create_key_field(struct hist_trigger_data *hist_data,
if (field_str) {
if (!strcmp(field_str, "hex"))
flags |= HIST_FIELD_HEX;
+ else if (!strcmp(field_str, "sym"))
+ flags |= HIST_FIELD_SYM;
+ else if (!strcmp(field_str, "sym-offset"))
+ flags |= HIST_FIELD_SYM_OFFSET;
else {
ret = -EINVAL;
goto out;
@@ -700,6 +706,7 @@ hist_trigger_entry_print(struct seq_file *m,
struct tracing_map_elt *elt)
{
struct hist_field *key_field;
+ char str[KSYM_SYMBOL_LEN];
unsigned int i;
u64 uval;
@@ -715,6 +722,16 @@ hist_trigger_entry_print(struct seq_file *m,
uval = *(u64 *)(key + key_field->offset);
seq_printf(m, "%s: %llx",
key_field->field->name, uval);
+ } else if (key_field->flags & HIST_FIELD_SYM) {
+ uval = *(u64 *)(key + key_field->offset);
+ sprint_symbol_no_offset(str, uval);
+ seq_printf(m, "%s: [%llx] %-45s",
+ key_field->field->name, uval, str);
+ } else if (key_field->flags & HIST_FIELD_SYM_OFFSET) {
+ uval = *(u64 *)(key + key_field->offset);
+ sprint_symbol(str, uval);
+ seq_printf(m, "%s: [%llx] %-55s",
+ key_field->field->name, uval, str);
} else if (key_field->flags & HIST_FIELD_STRING) {
seq_printf(m, "%s: %-35s", key_field->field->name,
(char *)(key + key_field->offset));
@@ -824,6 +841,10 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
if (hist_field->flags & HIST_FIELD_HEX)
flags_str = "hex";
+ else if (hist_field->flags & HIST_FIELD_SYM)
+ flags_str = "sym";
+ else if (hist_field->flags & HIST_FIELD_SYM_OFFSET)
+ flags_str = "sym-offset";
return flags_str;
}
--
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-07-16 19:30 +0200 |
| Subject | [PATCH v9 16/22] tracing: Add hist trigger 'execname' modifier |
| Message-ID | <pMV8T-4VI-49@gated-at.bofh.it> |
| In reply to | #1186095 |
Allow users to have pid fields displayed as program names in the output
by appending '.execname' to field names:
# echo hist:keys=aaa.execname ... \
[ if filter] > event/trigger
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.c | 1 +
kernel/trace/trace_events_hist.c | 86 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8cdc7b3..a16ab69 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3816,6 +3816,7 @@ static const char readme_msg[] =
"\t .hex display a number as a hex value\n\n"
"\t .sym display an address as a symbol\n"
"\t .sym-offset display an address as a symbol and offset\n"
+ "\t .execname display a common_pid as a program name\n\n"
"\t By default, the size of the hash table is 2048 entries. The\n"
"\t 'size' param can be used to specify more or fewer than that.\n"
"\t The units are in terms of hashtable entries - if a run uses\n"
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 106d557..af1b846 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -75,6 +75,7 @@ enum hist_field_flags {
HIST_FIELD_HEX = 8,
HIST_FIELD_SYM = 16,
HIST_FIELD_SYM_OFFSET = 32,
+ HIST_FIELD_EXECNAME = 64,
};
struct hist_trigger_attrs {
@@ -218,6 +219,78 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
return ERR_PTR(ret);
}
+static inline void save_comm(char *comm, struct task_struct *task)
+{
+ if (!task->pid) {
+ strcpy(comm, "<idle>");
+ return;
+ }
+
+ if (WARN_ON_ONCE(task->pid < 0)) {
+ strcpy(comm, "<XXX>");
+ return;
+ }
+
+ if (task->pid > PID_MAX_DEFAULT) {
+ strcpy(comm, "<...>");
+ return;
+ }
+
+ memcpy(comm, task->comm, TASK_COMM_LEN);
+}
+
+static void hist_trigger_elt_free(struct tracing_map_elt *elt)
+{
+ kfree((char *)elt->private_data);
+}
+
+static int hist_trigger_elt_alloc(struct tracing_map_elt *elt)
+{
+ struct hist_trigger_data *hist_data = elt->map->private_data;
+ struct hist_field *key_field;
+ unsigned int i;
+
+ for (i = hist_data->n_vals; i < hist_data->n_fields; i++) {
+ key_field = hist_data->fields[i];
+
+ if (key_field->flags & HIST_FIELD_EXECNAME) {
+ unsigned int size = TASK_COMM_LEN + 1;
+
+ elt->private_data = kzalloc(size, GFP_KERNEL);
+ if (!elt->private_data)
+ return -ENOMEM;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void hist_trigger_elt_copy(struct tracing_map_elt *to,
+ struct tracing_map_elt *from)
+{
+ char *comm_from = from->private_data;
+ char *comm_to = to->private_data;
+
+ if (comm_from)
+ memcpy(comm_to, comm_from, TASK_COMM_LEN + 1);
+}
+
+static void hist_trigger_elt_init(struct tracing_map_elt *elt)
+{
+ char *comm = elt->private_data;
+
+ if (comm)
+ save_comm(comm, current);
+}
+
+static struct tracing_map_ops hist_trigger_ops = {
+ .elt_alloc = hist_trigger_elt_alloc,
+ .elt_copy = hist_trigger_elt_copy,
+ .elt_free = hist_trigger_elt_free,
+ .elt_init = hist_trigger_elt_init,
+};
+
static void destroy_hist_field(struct hist_field *hist_field)
{
kfree(hist_field);
@@ -376,6 +449,9 @@ static int create_key_field(struct hist_trigger_data *hist_data,
flags |= HIST_FIELD_SYM;
else if (!strcmp(field_str, "sym-offset"))
flags |= HIST_FIELD_SYM_OFFSET;
+ else if (!strcmp(field_str, "execname") &&
+ !strcmp(field_name, "common_pid"))
+ flags |= HIST_FIELD_EXECNAME;
else {
ret = -EINVAL;
goto out;
@@ -612,7 +688,7 @@ create_hist_data(unsigned int map_bits,
goto free;
hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
- NULL, hist_data);
+ &hist_trigger_ops, hist_data);
if (IS_ERR(hist_data->map)) {
ret = PTR_ERR(hist_data->map);
hist_data->map = NULL;
@@ -732,6 +808,12 @@ hist_trigger_entry_print(struct seq_file *m,
sprint_symbol(str, uval);
seq_printf(m, "%s: [%llx] %-55s",
key_field->field->name, uval, str);
+ } else if (key_field->flags & HIST_FIELD_EXECNAME) {
+ char *comm = elt->private_data;
+
+ uval = *(u64 *)(key + key_field->offset);
+ seq_printf(m, "%s: %-16s[%10llu]",
+ key_field->field->name, comm, uval);
} else if (key_field->flags & HIST_FIELD_STRING) {
seq_printf(m, "%s: %-35s", key_field->field->name,
(char *)(key + key_field->offset));
@@ -845,6 +927,8 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
flags_str = "sym";
else if (hist_field->flags & HIST_FIELD_SYM_OFFSET)
flags_str = "sym-offset";
+ else if (hist_field->flags & HIST_FIELD_EXECNAME)
+ flags_str = "execname";
return flags_str;
}
--
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-07-16 19:30 +0200 |
| Subject | [PATCH v9 11/22] tracing: Add hist trigger support for user-defined sorting ('sort=' param) |
| Message-ID | <pMV8T-4VI-51@gated-at.bofh.it> |
| In reply to | #1186095 |
Allow users to specify keys and/or values to sort on. With this
addition, keys and values specified using the 'keys=' and 'vals='
keywords can be used to sort the hist trigger output via a new 'sort='
keyword. If multiple sort keys are specified, the output will be
sorted using the second key as a secondary sort key, etc. The default
sort order is ascending; if the user wants a different sort order,
'.descending' can be appended to the specific sort key. Before this
addition, output was always sorted by 'hitcount' in ascending order.
This expands the hist trigger syntax from this:
# echo hist:keys=xxx:vals=yyy \
[ if filter] > event/trigger
to this:
# echo hist:keys=xxx:vals=yyy:sort=zzz.descending \
[ if filter] > event/trigger
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.c | 10 ++--
kernel/trace/trace_events_hist.c | 101 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 107 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 1e4801e..5dd1fc4 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3789,6 +3789,7 @@ static const char readme_msg[] =
" hist trigger\t- If set, event hits are aggregated into a hash table\n"
"\t Format: hist:keys=<field1>[,field2,...]\n"
"\t [:values=<field1[,field2,...]]\n"
+ "\t [:sort=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"
@@ -3801,7 +3802,8 @@ static const char readme_msg[] =
"\t If 'values' is not specified, 'hitcount' will be assumed.\n"
"\t of event hits. Keys can be any field. Compound keys\n"
"\t consisting of up to two fields can be specified by the 'keys'\n"
- "\t keyword.\n\n"
+ "\t keyword. Additionally, sort keys consisting of up to two\n"
+ "\t fields can be specified by the 'sort' keyword.\n\n"
"\t Reading the 'hist' file for the event will dump the hash\n"
"\t table in its entirety to stdout. Each printed hash table\n"
"\t entry is a simple list of the keys and values comprising the\n"
@@ -3815,8 +3817,10 @@ static const char readme_msg[] =
"\t of 'drops', the number of hits that were ignored. The size\n"
"\t should be a power of 2 between 128 and 131072 (any non-\n"
"\t power-of-2 number specified will be rounded up).\n\n"
- "\t The entries are sorted by 'hitcount' and the sort order is\n"
- "\t 'ascending'.\n\n"
+ "\t The 'sort' param can be used to specify a value field to sort\n"
+ "\t on. The default if unspecified is 'hitcount' and the.\n"
+ "\t default sort order is 'ascending'. To sort in the opposite\n"
+ "\t direction, append .descending' to the sort key.\n\n"
#endif
;
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 3d5433a..6bf224f 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -77,6 +77,7 @@ enum hist_field_flags {
struct hist_trigger_attrs {
char *keys_str;
char *vals_str;
+ char *sort_key_str;
unsigned int map_bits;
};
@@ -156,6 +157,7 @@ static int parse_map_size(char *str)
static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
{
+ kfree(attrs->sort_key_str);
kfree(attrs->keys_str);
kfree(attrs->vals_str);
kfree(attrs);
@@ -180,6 +182,8 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
!strncmp(str, "vals", strlen("vals")) ||
!strncmp(str, "val", strlen("val")))
attrs->vals_str = kstrdup(str, GFP_KERNEL);
+ else if (!strncmp(str, "sort", strlen("sort")))
+ attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
else if (!strncmp(str, "size", strlen("size"))) {
int map_bits = parse_map_size(str);
@@ -420,12 +424,88 @@ static int create_hist_fields(struct hist_trigger_data *hist_data,
return ret;
}
+static int is_descending(const char *str)
+{
+ if (!str)
+ return 0;
+
+ if (!strcmp(str, "descending"))
+ return 1;
+
+ if (!strcmp(str, "ascending"))
+ return 0;
+
+ return -EINVAL;
+}
+
static int create_sort_keys(struct hist_trigger_data *hist_data)
{
+ char *fields_str = hist_data->attrs->sort_key_str;
+ struct ftrace_event_field *field = NULL;
+ struct tracing_map_sort_key *sort_key;
+ unsigned int i, j;
int ret = 0;
hist_data->n_sort_keys = 1; /* sort_keys[0] is always hitcount */
+ if (!fields_str)
+ goto out;
+
+ strsep(&fields_str, "=");
+ if (!fields_str) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < TRACING_MAP_SORT_KEYS_MAX; i++) {
+ char *field_str, *field_name;
+
+ sort_key = &hist_data->sort_keys[i];
+
+ field_str = strsep(&fields_str, ",");
+ if (!field_str) {
+ if (i == 0)
+ ret = -EINVAL;
+ break;
+ }
+
+ if ((i == TRACING_MAP_SORT_KEYS_MAX - 1) && fields_str) {
+ ret = -EINVAL;
+ break;
+ }
+
+ field_name = strsep(&field_str, ".");
+ if (!field_name) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!strcmp(field_name, "hitcount")) {
+ ret = is_descending(field_str);
+ if (ret < 0)
+ break;
+ sort_key->descending = ret;
+ continue;
+ }
+
+ for (j = 1; j < hist_data->n_fields; j++) {
+ field = hist_data->fields[j]->field;
+ if (field && !strcmp(field_name, field->name)) {
+ sort_key->field_idx = j;
+ ret = is_descending(field_str);
+ if (ret < 0)
+ goto out;
+ sort_key->descending = ret;
+ break;
+ }
+ }
+ if (j == hist_data->n_fields) {
+ ret = -EINVAL;
+ break;
+ }
+ }
+ hist_data->n_sort_keys = i;
+ out:
return ret;
}
@@ -737,7 +817,26 @@ static int event_hist_trigger_print(struct seq_file *m,
}
seq_puts(m, ":sort=");
- seq_puts(m, "hitcount");
+
+ for (i = 0; i < hist_data->n_sort_keys; i++) {
+ struct tracing_map_sort_key *sort_key;
+
+ sort_key = &hist_data->sort_keys[i];
+
+ if (i > 0)
+ seq_puts(m, ",");
+
+ if (sort_key->field_idx == HITCOUNT_IDX)
+ seq_puts(m, "hitcount");
+ else {
+ unsigned int idx = sort_key->field_idx;
+
+ hist_field_print(m, hist_data->fields[idx]);
+ }
+
+ if (sort_key->descending)
+ seq_puts(m, ".descending");
+ }
seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits));
--
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-07-16 19:30 +0200 |
| Subject | [PATCH v9 12/22] tracing: Add hist trigger support for pausing and continuing a trace |
| Message-ID | <pMV8T-4VI-53@gated-at.bofh.it> |
| In reply to | #1186095 |
Allow users to append 'pause' or 'continue' to an existing trigger in
order to have it paused or to have a paused trace continue.
This expands the hist trigger syntax from this:
# echo hist:keys=xxx:vals=yyy:sort=zzz.descending \
[ if filter] > event/trigger
to this:
# echo hist:keys=xxx:vals=yyy:sort=zzz.descending:pause or cont \
[ if filter] > event/trigger
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.c | 5 +++++
kernel/trace/trace_events_hist.c | 26 +++++++++++++++++++++++---
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 5dd1fc4..547bbc8 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3791,6 +3791,7 @@ static const char readme_msg[] =
"\t [:values=<field1[,field2,...]]\n"
"\t [:sort=field1,field2,...]\n"
"\t [:size=#entries]\n"
+ "\t [:pause][:continue]\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(s) and value(s) named. Keys and values\n"
@@ -3821,6 +3822,10 @@ static const char readme_msg[] =
"\t on. The default if unspecified is 'hitcount' and the.\n"
"\t default sort order is 'ascending'. To sort in the opposite\n"
"\t direction, append .descending' to the sort key.\n\n"
+ "\t The 'pause' param can be used to pause an existing hist\n"
+ "\t trigger or to start a hist trigger but not log any events\n"
+ "\t until told to do so. 'continue' can be used to start or\n"
+ "\t restart a paused hist trigger.\n\n"
#endif
;
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 6bf224f..3ae58e7 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -78,6 +78,8 @@ struct hist_trigger_attrs {
char *keys_str;
char *vals_str;
char *sort_key_str;
+ bool pause;
+ bool cont;
unsigned int map_bits;
};
@@ -184,6 +186,11 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
attrs->vals_str = kstrdup(str, GFP_KERNEL);
else if (!strncmp(str, "sort", strlen("sort")))
attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
+ else if (!strncmp(str, "pause", strlen("pause")))
+ attrs->pause = true;
+ else if (!strncmp(str, "continue", strlen("continue")) ||
+ !strncmp(str, "cont", strlen("cont")))
+ attrs->cont = true;
else if (!strncmp(str, "size", strlen("size"))) {
int map_bits = parse_map_size(str);
@@ -843,7 +850,10 @@ static int event_hist_trigger_print(struct seq_file *m,
if (data->filter_str)
seq_printf(m, " if %s", data->filter_str);
- seq_puts(m, " [active]");
+ if (data->paused)
+ seq_puts(m, " [paused]");
+ else
+ seq_puts(m, " [active]");
seq_putc(m, '\n');
@@ -882,16 +892,25 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
struct event_trigger_data *data,
struct trace_event_file *file)
{
+ struct hist_trigger_data *hist_data = data->private_data;
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;
+ if (hist_data->attrs->pause)
+ test->paused = true;
+ else if (hist_data->attrs->cont)
+ test->paused = false;
+ else
+ ret = -EEXIST;
goto out;
}
}
+ if (hist_data->attrs->pause)
+ data->paused = true;
+
if (data->ops->init) {
ret = data->ops->init(data->ops, data);
if (ret < 0)
@@ -984,7 +1003,8 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
* triggers registered a failure too.
*/
if (!ret) {
- ret = -ENOENT;
+ if (!(attrs->pause || attrs->cont))
+ ret = -ENOENT;
goto out_free;
} else if (ret < 0)
goto out_free;
--
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 | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> |
|---|---|
| Date | 2015-07-22 10:30 +0200 |
| Subject | Re: [PATCH v9 12/22] tracing: Add hist trigger support for pausing and continuing a trace |
| Message-ID | <pOXzA-1mZ-13@gated-at.bofh.it> |
| In reply to | #1186102 |
Hi Tom,
On 2015/07/17 2:22, Tom Zanussi wrote:
> Allow users to append 'pause' or 'continue' to an existing trigger in
> order to have it paused or to have a paused trace continue.
>
> This expands the hist trigger syntax from this:
> # echo hist:keys=xxx:vals=yyy:sort=zzz.descending \
> [ if filter] > event/trigger
>
> to this:
>
> # echo hist:keys=xxx:vals=yyy:sort=zzz.descending:pause or cont \
> [ if filter] > event/trigger
Since the only one hist trigger can be set on one event, it seems
that we don't need keys for pause/cont/clear (e.g. hist:pause is enough).
Anyway, I've found an odd behavior.
[root@localhost tracing]# echo 'hist:keys=parent_pid' > events/sched/sched_process_fork/trigger
[root@localhost tracing]# echo 'hist:keys=common_pid:pause' > events/sched/sched_process_fork/trigger
[root@localhost tracing]# cat events/sched/sched_process_fork/trigger
hist:keys=parent_pid:vals=hitcount:sort=hitcount:size=2048 [paused]
So, the second "pause" command can work with different keys.
Moreover, I can remove it with different keys.
[root@localhost tracing]# echo '!hist:keys=child_pid' > events/sched/sched_process_fork/trigger
[root@localhost tracing]# cat events/sched/sched_process_fork/trigger
# Available triggers:
# traceon traceoff snapshot stacktrace enable_event disable_event enable_hist disable_hist hist
Thank you,
>
> Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
> ---
> kernel/trace/trace.c | 5 +++++
> kernel/trace/trace_events_hist.c | 26 +++++++++++++++++++++++---
> 2 files changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 5dd1fc4..547bbc8 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -3791,6 +3791,7 @@ static const char readme_msg[] =
> "\t [:values=<field1[,field2,...]]\n"
> "\t [:sort=field1,field2,...]\n"
> "\t [:size=#entries]\n"
> + "\t [:pause][:continue]\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(s) and value(s) named. Keys and values\n"
> @@ -3821,6 +3822,10 @@ static const char readme_msg[] =
> "\t on. The default if unspecified is 'hitcount' and the.\n"
> "\t default sort order is 'ascending'. To sort in the opposite\n"
> "\t direction, append .descending' to the sort key.\n\n"
> + "\t The 'pause' param can be used to pause an existing hist\n"
> + "\t trigger or to start a hist trigger but not log any events\n"
> + "\t until told to do so. 'continue' can be used to start or\n"
> + "\t restart a paused hist trigger.\n\n"
> #endif
> ;
>
> diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
> index 6bf224f..3ae58e7 100644
> --- a/kernel/trace/trace_events_hist.c
> +++ b/kernel/trace/trace_events_hist.c
> @@ -78,6 +78,8 @@ struct hist_trigger_attrs {
> char *keys_str;
> char *vals_str;
> char *sort_key_str;
> + bool pause;
> + bool cont;
> unsigned int map_bits;
> };
>
> @@ -184,6 +186,11 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
> attrs->vals_str = kstrdup(str, GFP_KERNEL);
> else if (!strncmp(str, "sort", strlen("sort")))
> attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
> + else if (!strncmp(str, "pause", strlen("pause")))
> + attrs->pause = true;
> + else if (!strncmp(str, "continue", strlen("continue")) ||
> + !strncmp(str, "cont", strlen("cont")))
> + attrs->cont = true;
> else if (!strncmp(str, "size", strlen("size"))) {
> int map_bits = parse_map_size(str);
>
> @@ -843,7 +850,10 @@ static int event_hist_trigger_print(struct seq_file *m,
> if (data->filter_str)
> seq_printf(m, " if %s", data->filter_str);
>
> - seq_puts(m, " [active]");
> + if (data->paused)
> + seq_puts(m, " [paused]");
> + else
> + seq_puts(m, " [active]");
>
> seq_putc(m, '\n');
>
> @@ -882,16 +892,25 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
> struct event_trigger_data *data,
> struct trace_event_file *file)
> {
> + struct hist_trigger_data *hist_data = data->private_data;
> 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;
> + if (hist_data->attrs->pause)
> + test->paused = true;
> + else if (hist_data->attrs->cont)
> + test->paused = false;
> + else
> + ret = -EEXIST;
> goto out;
> }
> }
>
> + if (hist_data->attrs->pause)
> + data->paused = true;
> +
> if (data->ops->init) {
> ret = data->ops->init(data->ops, data);
> if (ret < 0)
> @@ -984,7 +1003,8 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
> * triggers registered a failure too.
> */
> if (!ret) {
> - ret = -ENOENT;
> + if (!(attrs->pause || attrs->cont))
> + ret = -ENOENT;
> goto out_free;
> } else if (ret < 0)
> goto out_free;
>
--
Masami HIRAMATSU
Linux Technology Research Center, System Productivity Research Dept.
Center for Technology Innovation - Systems Engineering
Hitachi, Ltd., Research & Development Group
E-mail: masami.hiramatsu.pt@hitachi.com
--
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-07-16 19:30 +0200 |
| Subject | [PATCH v9 08/22] tracing: Add 'hist' event trigger command |
| Message-ID | <pMV8U-4VI-57@gated-at.bofh.it> |
| In reply to | #1186095 |
'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>
---
include/linux/trace_events.h | 1 +
kernel/trace/Kconfig | 14 +
kernel/trace/Makefile | 1 +
kernel/trace/trace.c | 29 ++
kernel/trace/trace.h | 7 +
kernel/trace/trace_events.c | 4 +
kernel/trace/trace_events_hist.c | 832 ++++++++++++++++++++++++++++++++++++
kernel/trace/trace_events_trigger.c | 1 +
8 files changed, 889 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 d9b0f89..0faf48b 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -410,6 +410,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 3b9a48a..85f8025 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -528,6 +528,20 @@ config MMIOTRACE
See Documentation/trace/mmiotrace.txt.
If you are not helping to develop drivers, say N.
+config HIST_TRIGGERS
+ bool "Histogram triggers"
+ depends on ARCH_HAVE_NMI_SAFE_CMPXCHG
+ 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
tristate "Test module for mmiotrace"
depends on MMIOTRACE && m
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 3b26cfb..7faace3 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 abcbf7f..f6fdda2 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3767,6 +3767,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"
@@ -3782,6 +3785,32 @@ 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. Keys correspond to fields in the\n"
+ "\t event's format description. On an event hit, the value of a\n"
+ "\t sum called 'hitcount' is incremented, which is simply a count\n"
+ "\t of event hits. Keys can be any field.\n\n"
+ "\t Reading the 'hist' file for the event will dump the hash\n"
+ "\t table in its entirety to stdout. Each printed hash table\n"
+ "\t entry is a simple list of the keys and values comprising the\n"
+ "\t entry; keys are printed first and are delineated by curly\n"
+ "\t braces, and are followed by the set of value fields for the\n"
+ "\t entry. Numeric fields are displayed as base-10 integers.\n"
+ "\t By default, the size of the hash table is 2048 entries. The\n"
+ "\t 'size' param can be used to specify more or fewer than that.\n"
+ "\t The units are in terms of hashtable entries - if a run uses\n"
+ "\t more entries than specified, the results will show the number\n"
+ "\t of 'drops', the number of hits that were ignored. The size\n"
+ "\t should be a power of 2 between 128 and 131072 (any non-\n"
+ "\t power-of-2 number specified will be rounded up).\n\n"
+ "\t The entries are sorted by 'hitcount' and the sort order is\n"
+ "\t 'ascending'.\n\n"
+#endif
;
static ssize_t
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 5e675b2..e6cb781 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1098,6 +1098,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 404a372..2bf0465 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1628,6 +1628,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..ded348b
--- /dev/null
+++ b/kernel/trace/trace_events_hist.c
@@ -0,0 +1,832 @@
+/*
+ * 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)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 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 {
+ atomic64_t total_hits;
+ 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;
+ atomic64_t drops;
+ 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)
+{
+ 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;
+ }
+ }
+
+ 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 (i = 0; i < hist_data->n_fields; i++) {
+ 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 (i = 0; i < hist_data->n_fields; i++) {
+ 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:
+ 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 (i = 0; i < hist_data->n_vals; i++) {
+ 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;
+
+ if (atomic64_read(&hist_data->drops)) {
+ atomic64_inc(&hist_data->drops);
+ return;
+ }
+
+ for (i = hist_data->n_vals; i < hist_data->n_fields; i++) {
+ key_field = hist_data->fields[i];
+
+ field_contents = key_field->fn(key_field, rec);
+ if (key_field->flags & HIST_FIELD_STRING)
+ key = (void *)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);
+ else
+ atomic64_inc(&hist_data->drops);
+
+ atomic64_inc(&hist_data->total_hits);
+}
+
+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 (i = hist_data->n_vals; i < hist_data->n_fields; i++) {
+ 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: %-35s", 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, "# 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: %lu\n Entries: %u\n Dropped: %lu\n",
+ atomic64_read(&hist_data->total_hits),
+ n_entries, atomic64_read(&hist_data->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 (i = hist_data->n_vals; i < hist_data->n_fields; i++) {
+ 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->keys_str)
+ return -EINVAL;
+
+ 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))
+ 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;
+
+ 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,
+ .post_trigger = true, /* need non-NULL rec */
+ .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 5f632ff..e80f30b 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -1437,6 +1437,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 | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> |
|---|---|
| Date | 2015-07-20 15:40 +0200 |
| Subject | Re: [PATCH v9 08/22] tracing: Add 'hist' event trigger command |
| Message-ID | <pOjsv-2E2-5@gated-at.bofh.it> |
| In reply to | #1186103 |
Hi Tom, On 2015/07/17 2:22, Tom Zanussi wrote: > @@ -3782,6 +3785,32 @@ 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. Keys correspond to fields in the\n" > + "\t event's format description. On an event hit, the value of a\n" > + "\t sum called 'hitcount' is incremented, which is simply a count\n" > + "\t of event hits. Keys can be any field.\n\n" > + "\t Reading the 'hist' file for the event will dump the hash\n" > + "\t table in its entirety to stdout. Each printed hash table\n" > + "\t entry is a simple list of the keys and values comprising the\n" > + "\t entry; keys are printed first and are delineated by curly\n" > + "\t braces, and are followed by the set of value fields for the\n" > + "\t entry. Numeric fields are displayed as base-10 integers.\n" > + "\t By default, the size of the hash table is 2048 entries. The\n" > + "\t 'size' param can be used to specify more or fewer than that.\n" > + "\t The units are in terms of hashtable entries - if a run uses\n" > + "\t more entries than specified, the results will show the number\n" > + "\t of 'drops', the number of hits that were ignored. The size\n" > + "\t should be a power of 2 between 128 and 131072 (any non-\n" > + "\t power-of-2 number specified will be rounded up).\n\n" > + "\t The entries are sorted by 'hitcount' and the sort order is\n" > + "\t 'ascending'.\n\n" Hmm, this seems too much about implementation of histogram. Could you shorten this to be a half ? Thank you, -- Masami HIRAMATSU Linux Technology Research Center, System Productivity Research Dept. Center for Technology Innovation - Systems Engineering Hitachi, Ltd., Research & Development Group E-mail: masami.hiramatsu.pt@hitachi.com -- 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-07-16 19:30 +0200 |
| Subject | [PATCH v9 14/22] tracing: Add hist trigger 'hex' modifier for displaying numeric fields |
| Message-ID | <pMV8U-4VI-61@gated-at.bofh.it> |
| In reply to | #1186095 |
Allow users to have numeric fields displayed as hex values in the
output by appending '.hex' to field names:
# echo hist:keys=aaa,bbb.hex:vals=ccc.hex ... \
[ if filter] > event/trigger
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.c | 5 +++-
kernel/trace/trace_events_hist.c | 49 +++++++++++++++++++++++++++++++++++++---
2 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 27daa28..14f9472 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3810,7 +3810,10 @@ static const char readme_msg[] =
"\t entry is a simple list of the keys and values comprising the\n"
"\t entry; keys are printed first and are delineated by curly\n"
"\t braces, and are followed by the set of value fields for the\n"
- "\t entry. Numeric fields are displayed as base-10 integers.\n"
+ "\t entry. By default, numeric fields are displayed as base-10\n"
+ "\t integers. This can be modified by appending any of the\n"
+ "\t following modifiers to the field name:\n\n"
+ "\t .hex display a number as a hex value\n\n"
"\t By default, the size of the hash table is 2048 entries. The\n"
"\t 'size' param can be used to specify more or fewer than that.\n"
"\t The units are in terms of hashtable entries - if a run uses\n"
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index d8259fe..9cc38ee 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -72,6 +72,7 @@ enum hist_field_flags {
HIST_FIELD_HITCOUNT = 1,
HIST_FIELD_KEY = 2,
HIST_FIELD_STRING = 4,
+ HIST_FIELD_HEX = 8,
};
struct hist_trigger_attrs {
@@ -284,9 +285,20 @@ static int create_val_field(struct hist_trigger_data *hist_data,
{
struct ftrace_event_field *field = NULL;
unsigned long flags = 0;
+ char *field_name;
int ret = 0;
- field = trace_find_event_field(file->event_call, field_str);
+ field_name = strsep(&field_str, ".");
+ if (field_str) {
+ if (!strcmp(field_str, "hex"))
+ flags |= HIST_FIELD_HEX;
+ else {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ field = trace_find_event_field(file->event_call, field_name);
if (!field) {
ret = -EINVAL;
goto out;
@@ -349,11 +361,22 @@ static int create_key_field(struct hist_trigger_data *hist_data,
struct ftrace_event_field *field = NULL;
unsigned long flags = 0;
unsigned int key_size;
+ char *field_name;
int ret = 0;
flags |= HIST_FIELD_KEY;
- field = trace_find_event_field(file->event_call, field_str);
+ field_name = strsep(&field_str, ".");
+ if (field_str) {
+ if (!strcmp(field_str, "hex"))
+ flags |= HIST_FIELD_HEX;
+ else {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ field = trace_find_event_field(file->event_call, field_name);
if (!field) {
ret = -EINVAL;
goto out;
@@ -688,7 +711,11 @@ hist_trigger_entry_print(struct seq_file *m,
if (i > hist_data->n_vals)
seq_puts(m, ", ");
- if (key_field->flags & HIST_FIELD_STRING) {
+ if (key_field->flags & HIST_FIELD_HEX) {
+ uval = *(u64 *)(key + key_field->offset);
+ seq_printf(m, "%s: %llx",
+ key_field->field->name, uval);
+ } else if (key_field->flags & HIST_FIELD_STRING) {
seq_printf(m, "%s: %-35s", key_field->field->name,
(char *)(key + key_field->offset));
} else {
@@ -791,9 +818,25 @@ const struct file_operations event_hist_fops = {
.release = single_release,
};
+static const char *get_hist_field_flags(struct hist_field *hist_field)
+{
+ const char *flags_str = NULL;
+
+ if (hist_field->flags & HIST_FIELD_HEX)
+ flags_str = "hex";
+
+ return flags_str;
+}
+
static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
{
seq_printf(m, "%s", hist_field->field->name);
+ if (hist_field->flags) {
+ const char *flags_str = get_hist_field_flags(hist_field);
+
+ if (flags_str)
+ seq_printf(m, ".%s", flags_str);
+ }
}
static int event_hist_trigger_print(struct seq_file *m,
--
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-07-16 19:30 +0200 |
| Subject | [PATCH v9 17/22] tracing: Add hist trigger 'syscall' modifier |
| Message-ID | <pMV8U-4VI-69@gated-at.bofh.it> |
| In reply to | #1186095 |
Allow users to have syscall id fields displayed as syscall names in
the output by appending '.syscall' to field names:
# echo hist:keys=aaa.syscall ... \
[ if filter] > event/trigger
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.c | 1 +
kernel/trace/trace_events_hist.c | 15 +++++++++++++++
2 files changed, 16 insertions(+)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index a16ab69..75795e3 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3817,6 +3817,7 @@ static const char readme_msg[] =
"\t .sym display an address as a symbol\n"
"\t .sym-offset display an address as a symbol and offset\n"
"\t .execname display a common_pid as a program name\n\n"
+ "\t .syscall display a syscall id as a syscall name\n"
"\t By default, the size of the hash table is 2048 entries. The\n"
"\t 'size' param can be used to specify more or fewer than that.\n"
"\t The units are in terms of hashtable entries - if a run uses\n"
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index af1b846..28ccaa1 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -76,6 +76,7 @@ enum hist_field_flags {
HIST_FIELD_SYM = 16,
HIST_FIELD_SYM_OFFSET = 32,
HIST_FIELD_EXECNAME = 64,
+ HIST_FIELD_SYSCALL = 128,
};
struct hist_trigger_attrs {
@@ -452,6 +453,8 @@ static int create_key_field(struct hist_trigger_data *hist_data,
else if (!strcmp(field_str, "execname") &&
!strcmp(field_name, "common_pid"))
flags |= HIST_FIELD_EXECNAME;
+ else if (!strcmp(field_str, "syscall"))
+ flags |= HIST_FIELD_SYSCALL;
else {
ret = -EINVAL;
goto out;
@@ -814,6 +817,16 @@ hist_trigger_entry_print(struct seq_file *m,
uval = *(u64 *)(key + key_field->offset);
seq_printf(m, "%s: %-16s[%10llu]",
key_field->field->name, comm, uval);
+ } else if (key_field->flags & HIST_FIELD_SYSCALL) {
+ const char *syscall_name;
+
+ uval = *(u64 *)(key + key_field->offset);
+ syscall_name = get_syscall_name(uval);
+ if (!syscall_name)
+ syscall_name = "unknown_syscall";
+
+ seq_printf(m, "%s: %-30s[%3llu]",
+ key_field->field->name, syscall_name, uval);
} else if (key_field->flags & HIST_FIELD_STRING) {
seq_printf(m, "%s: %-35s", key_field->field->name,
(char *)(key + key_field->offset));
@@ -929,6 +942,8 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
flags_str = "sym-offset";
else if (hist_field->flags & HIST_FIELD_EXECNAME)
flags_str = "execname";
+ else if (hist_field->flags & HIST_FIELD_SYSCALL)
+ flags_str = "syscall";
return flags_str;
}
--
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-07-16 19:30 +0200 |
| Subject | [PATCH v9 04/22] tracing: Add event record param to trigger_ops.func() |
| Message-ID | <pMV8U-4VI-67@gated-at.bofh.it> |
| In reply to | #1186095 |
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>
---
include/linux/trace_events.h | 7 ++++---
kernel/trace/trace.h | 6 ++++--
kernel/trace/trace_events_trigger.c | 35 ++++++++++++++++++-----------------
3 files changed, 26 insertions(+), 22 deletions(-)
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 1063c85..d9b0f89 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -423,7 +423,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);
/**
* trace_trigger_soft_disabled - do triggers and test if soft disabled
@@ -506,7 +507,7 @@ event_trigger_unlock_commit(struct trace_event_file *file,
trace_buffer_unlock_commit(buffer, event, irq_flags, pc);
if (tt)
- event_triggers_post_call(file, tt);
+ event_triggers_post_call(file, tt, entry);
}
/**
@@ -539,7 +540,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_SYSCALL
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 4ff33b7..8799348 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1139,7 +1139,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()
@@ -1164,7 +1165,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..e30539c 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;
}
@@ -103,13 +103,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 +751,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 +760,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 +775,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 +784,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 +880,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 +894,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 +973,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 +987,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 +1058,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 +1069,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 +1083,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-07-16 19:30 +0200 |
| Subject | [PATCH v9 05/22] tracing: Add get_syscall_name() |
| Message-ID | <pMV8U-4VI-77@gated-at.bofh.it> |
| In reply to | #1186095 |
Add a utility function to grab the syscall name from the syscall
metadata, given a syscall id.
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
---
kernel/trace/trace.h | 5 +++++
kernel/trace/trace_syscalls.c | 11 +++++++++++
2 files changed, 16 insertions(+)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 8799348..6fe5b66 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1331,8 +1331,13 @@ int perf_ftrace_event_register(struct trace_event_call *call,
#ifdef CONFIG_FTRACE_SYSCALLS
void init_ftrace_syscalls(void);
+const char *get_syscall_name(int syscall);
#else
static inline void init_ftrace_syscalls(void) { }
+static inline const char *get_syscall_name(int syscall)
+{
+ return NULL;
+}
#endif
#ifdef CONFIG_EVENT_TRACING
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index 7d567a4..004c111 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -106,6 +106,17 @@ static struct syscall_metadata *syscall_nr_to_meta(int nr)
return syscalls_metadata[nr];
}
+const char *get_syscall_name(int syscall)
+{
+ struct syscall_metadata *entry;
+
+ entry = syscall_nr_to_meta(syscall);
+ if (!entry)
+ return NULL;
+
+ return entry->name;
+}
+
static enum print_line_t
print_syscall_enter(struct trace_iterator *iter, int flags,
struct trace_event *event)
--
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 | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2015-07-16 20:00 +0200 |
| Subject | Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map |
| Message-ID | <pMVBU-5ug-9@gated-at.bofh.it> |
| In reply to | #1186095 |
On Thu, Jul 16, 2015 at 12:22:40PM -0500, Tom Zanussi wrote:
> + for (i = 0; i < elt->map->n_fields; i++) {
> + atomic64_set(&dup_elt->fields[i].sum,
> + atomic64_read(&elt->fields[i].sum));
> + dup_elt->fields[i].cmp_fn = elt->fields[i].cmp_fn;
> + }
> +
> + return dup_elt;
> +}
So there is a lot of atomic64_{set,read}() in this patch set, what kind
of magic properties do you assume they have?
Note that atomic*_{set,read}() are weaker than {WRITE,READ}_ONCE(), so
if you're assuming they do that, you're mistaken -- although it is on a
TODO list someplace to go fix that.
--
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-07-16 23:50 +0200 |
| Subject | Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map |
| Message-ID | <pMZcu-2eG-5@gated-at.bofh.it> |
| In reply to | #1186157 |
On Thu, 2015-07-16 at 19:49 +0200, Peter Zijlstra wrote:
> On Thu, Jul 16, 2015 at 12:22:40PM -0500, Tom Zanussi wrote:
> > + for (i = 0; i < elt->map->n_fields; i++) {
> > + atomic64_set(&dup_elt->fields[i].sum,
> > + atomic64_read(&elt->fields[i].sum));
> > + dup_elt->fields[i].cmp_fn = elt->fields[i].cmp_fn;
> > + }
> > +
> > + return dup_elt;
> > +}
>
> So there is a lot of atomic64_{set,read}() in this patch set, what kind
> of magic properties do you assume they have?
>
> Note that atomic*_{set,read}() are weaker than {WRITE,READ}_ONCE(), so
> if you're assuming they do that, you're mistaken -- although it is on a
> TODO list someplace to go fix that.
Not assuming any magic properties - I just need an atomic 64-bit counter
for the sums and that's the API for setting/reading those. When reading
a live trace the exact sum you get is kind of arbitrary..
Tom
--
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 | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2015-07-17 00:40 +0200 |
| Subject | Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map |
| Message-ID | <pMZYS-3oO-27@gated-at.bofh.it> |
| In reply to | #1186277 |
On Thu, Jul 16, 2015 at 04:41:45PM -0500, Tom Zanussi wrote:
> On Thu, 2015-07-16 at 19:49 +0200, Peter Zijlstra wrote:
> > On Thu, Jul 16, 2015 at 12:22:40PM -0500, Tom Zanussi wrote:
> > > + for (i = 0; i < elt->map->n_fields; i++) {
> > > + atomic64_set(&dup_elt->fields[i].sum,
> > > + atomic64_read(&elt->fields[i].sum));
> > > + dup_elt->fields[i].cmp_fn = elt->fields[i].cmp_fn;
> > > + }
> > > +
> > > + return dup_elt;
> > > +}
> >
> > So there is a lot of atomic64_{set,read}() in this patch set, what kind
> > of magic properties do you assume they have?
> >
> > Note that atomic*_{set,read}() are weaker than {WRITE,READ}_ONCE(), so
> > if you're assuming they do that, you're mistaken -- although it is on a
> > TODO list someplace to go fix that.
>
> Not assuming any magic properties - I just need an atomic 64-bit counter
> for the sums and that's the API for setting/reading those. When reading
> a live trace the exact sum you get is kind of arbitrary..
OK, so atomic64_read() really should provide load consistency (there are
a few archs that lack the READ_ONCE() there).
But the atomic64_set() does not provide store consistency, and in the
above case it looks like the value you're writing is not exposed yet to
concurrency so it doesn't matter how it issues the store.
So as long as you never atomic64_set() a value that is subject to
concurrent modification you should be good.
--
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-07-17 04:00 +0200 |
| Subject | Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map |
| Message-ID | <pN36q-7Ts-7@gated-at.bofh.it> |
| In reply to | #1186300 |
On Fri, 2015-07-17 at 00:32 +0200, Peter Zijlstra wrote:
> On Thu, Jul 16, 2015 at 04:41:45PM -0500, Tom Zanussi wrote:
> > On Thu, 2015-07-16 at 19:49 +0200, Peter Zijlstra wrote:
> > > On Thu, Jul 16, 2015 at 12:22:40PM -0500, Tom Zanussi wrote:
> > > > + for (i = 0; i < elt->map->n_fields; i++) {
> > > > + atomic64_set(&dup_elt->fields[i].sum,
> > > > + atomic64_read(&elt->fields[i].sum));
> > > > + dup_elt->fields[i].cmp_fn = elt->fields[i].cmp_fn;
> > > > + }
> > > > +
> > > > + return dup_elt;
> > > > +}
> > >
> > > So there is a lot of atomic64_{set,read}() in this patch set, what kind
> > > of magic properties do you assume they have?
> > >
> > > Note that atomic*_{set,read}() are weaker than {WRITE,READ}_ONCE(), so
> > > if you're assuming they do that, you're mistaken -- although it is on a
> > > TODO list someplace to go fix that.
> >
> > Not assuming any magic properties - I just need an atomic 64-bit counter
> > for the sums and that's the API for setting/reading those. When reading
> > a live trace the exact sum you get is kind of arbitrary..
>
> OK, so atomic64_read() really should provide load consistency (there are
> a few archs that lack the READ_ONCE() there).
>
> But the atomic64_set() does not provide store consistency, and in the
> above case it looks like the value you're writing is not exposed yet to
> concurrency so it doesn't matter how it issues the store.
>
Right, that's correct.
> So as long as you never atomic64_set() a value that is subject to
> concurrent modification you should be good.
Yeah, and that's the case elsewhere as well.
Thanks for clarifying,
Tom
--
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 | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2015-07-16 20:10 +0200 |
| Subject | Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map |
| Message-ID | <pMVLA-5UN-21@gated-at.bofh.it> |
| In reply to | #1186095 |
On Thu, Jul 16, 2015 at 12:22:40PM -0500, Tom Zanussi wrote: > + map->map = kcalloc(map->map_size, sizeof(struct tracing_map_entry), > + GFP_KERNEL); In a later email you state the max map size to be 128k, with a 16 byte struct, that is 2m of memory for this allocation. Isn't that a tad big for a kmalloc() ? -- 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-07-16 23:40 +0200 |
| Subject | Re: [PATCH v9 07/22] tracing: Add lock-free tracing_map |
| Message-ID | <pMZ2O-23i-21@gated-at.bofh.it> |
| In reply to | #1186169 |
On Thu, 2015-07-16 at 20:03 +0200, Peter Zijlstra wrote: > On Thu, Jul 16, 2015 at 12:22:40PM -0500, Tom Zanussi wrote: > > + map->map = kcalloc(map->map_size, sizeof(struct tracing_map_entry), > > + GFP_KERNEL); > > In a later email you state the max map size to be 128k, with a 16 byte > struct, that is 2m of memory for this allocation. > > Isn't that a tad big for a kmalloc() ? Yeah, that is a bit big for kmalloc (actually it's double that), though I never ran into problems in my testing (of course that would depend on the state of the system, and I mainly tested on a newly booted system). It would probably make sense to make it page-based, which means a bit more complicated mapping for the array (can't use vmalloc here) but that shouldn't be too big a deal. Tom -- 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