tracing: 'hist' triggers
Subsystems (4)
PATCH v4 2/7 tracing: Add event record param to trigger_ops.func()
In reply to Tom ZanussiSome triggers may need access to the trace event, so pass it in. Also fix up the existing trigger funcs and their callers.
--- include/linux/ftrace_event.h | 7 ++++--- kernel/trace/trace.h | 6 ++++-- kernel/trace/trace_events_trigger.c | 35 ++++++++++++++++++----------------- 3 files changed, 26 insertions(+), 22 deletions(-)
include/linux/ftrace_event.h +4 -3
@@ -429,7 +429,8 @@ extern int call_filter_check_discard(struct ftrace_event_call *call, void *rec,extern enum event_trigger_type event_triggers_call (struct ftrace_event_file *file,void *rec);extern void event_triggers_post_call (struct ftrace_event_file *file, -enum event_trigger_type tt); +enum event_trigger_type tt, +void *rec);/** * ftrace_trigger_soft_disabled - do triggers and test if soft disabled
@@ -512,7 +513,7 @@ event_trigger_unlock_commit(struct ftrace_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); }/**
@@ -545,7 +546,7 @@ event_trigger_unlock_commit_regs(struct ftrace_event_file *file, irq_flags, pc, regs);if (tt) -event_triggers_post_call (file, tt); +event_triggers_post_call (file, tt, entry); }enum {
kernel/trace/trace.h +4 -2
@@ -1125,7 +1125,8 @@ struct event_trigger_data {* @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()
@@ -1150,7 +1151,8 @@ struct event_trigger_data {* (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,
kernel/trace/trace_events_trigger.c +18 -17
@@ -74,7 +74,7 @@ event_triggers_call(struct ftrace_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);
@@ -84,7 +84,7 @@ event_triggers_call(struct ftrace_event_file *file, void *rec) tt |= data->cmd_ops->trigger_type;continue ; } - data->ops->func (data); + data->ops->func (data, rec); }return tt; }
@@ -104,13 +104,14 @@ EXPORT_SYMBOL_GPL(event_triggers_call);*/ void event_triggers_post_call (struct ftrace_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);
@@ -751,7 +752,7 @@ static 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 ;
@@ -760,7 +761,7 @@ traceon_trigger(struct event_trigger_data *data) }static void -traceon_count_trigger (struct event_trigger_data *data) +traceon_count_trigger (struct event_trigger_data *data,void *rec) {if (tracing_is_on ())return ;
@@ -775,7 +776,7 @@ traceon_count_trigger(struct event_trigger_data *data) }static void -traceoff_trigger (struct event_trigger_data *data) +traceoff_trigger (struct event_trigger_data *data,void *rec) {if (!tracing_is_on ())return ;
@@ -784,7 +785,7 @@ traceoff_trigger(struct event_trigger_data *data) }static void -traceoff_count_trigger (struct event_trigger_data *data) +traceoff_count_trigger (struct event_trigger_data *data,void *rec) {if (!tracing_is_on ())return ;
@@ -880,13 +881,13 @@ static struct event_command trigger_traceoff_cmd = {#ifdef CONFIG_TRACER_SNAPSHOT static void -snapshot_trigger (struct event_trigger_data *data) +snapshot_trigger (struct event_trigger_data *data,void *rec) {tracing_snapshot (); }static void -snapshot_count_trigger (struct event_trigger_data *data) +snapshot_count_trigger (struct event_trigger_data *data,void *rec) {if (!data->count)return ;
@@ -894,7 +895,7 @@ snapshot_count_trigger(struct event_trigger_data *data)if (data->count !=-1 ) (data->count)--; -snapshot_trigger (data); +snapshot_trigger (data, rec); } static int
@@ -973,13 +974,13 @@ static __init int register_trigger_snapshot_cmd(void) { return 0; }#define STACK_SKIP 3static 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 ;
@@ -987,7 +988,7 @@ stacktrace_count_trigger(struct event_trigger_data *data)if (data->count !=-1 ) (data->count)--; -stacktrace_trigger (data); +stacktrace_trigger (data, rec); } static int
@@ -1058,7 +1059,7 @@ struct enable_trigger_data { };static void -event_enable_trigger (struct event_trigger_data *data) +event_enable_trigger (struct event_trigger_data *data,void *rec) {struct enable_trigger_data *enable_data = data->private_data;
@@ -1069,7 +1070,7 @@ event_enable_trigger(struct event_trigger_data *data) }static void -event_enable_count_trigger (struct event_trigger_data *data) +event_enable_count_trigger (struct event_trigger_data *data,void *rec) {struct enable_trigger_data *enable_data = data->private_data;
@@ -1083,7 +1084,7 @@ event_enable_count_trigger(struct event_trigger_data *data)if (data->count !=-1 ) (data->count)--; -event_enable_trigger (data); +event_enable_trigger (data, rec); } static int
Signature · 1 line
1.9.3
PATCH v4 4/7 tracing: Add a per-event-trigger 'paused' field
In reply to Tom ZanussiAdd a simple per-trigger 'paused' flag, allowing individual triggers to pause. We could leave it to individual triggers that need this functionality to do it themselves, but we also want to allow other events to control pausing, so add it to the trigger data.
--- kernel/trace/trace.h | 1 + kernel/trace/trace_events_trigger.c | 4 ++++ 2 files changed, 5 insertions(+)
kernel/trace/trace.h +1 -0
@@ -1110,6 +1110,7 @@ struct event_trigger_data {struct event_filter __rcu *filter;char *filter_str;void *private_data; +bool paused;struct list_head list; };
kernel/trace/trace_events_trigger.c +4 -0
@@ -73,6 +73,8 @@ event_triggers_call(struct ftrace_event_file *file, void *rec)return tt;list_for_each_entry_rcu (data, &file->triggers, list) { +if (data->paused) +continue ;if (!rec) { data->ops->func (data, rec);continue ;
@@ -110,6 +112,8 @@ event_triggers_post_call(struct ftrace_event_file *file,struct event_trigger_data *data;list_for_each_entry_rcu (data, &file->triggers, list) { +if (data->paused) +continue ;if (data->cmd_ops->trigger_type & tt) data->ops->func (data, rec); }
Signature · 1 line
1.9.3
PATCH v4 5/7 tracing: Add 'hist' event trigger command
In reply to Tom Zanussi'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:values=yyy [ 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 value fields specified.
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.
--- include/linux/ftrace_event.h | 1 + kernel/trace/Kconfig | 14 + kernel/trace/Makefile | 1 + kernel/trace/trace.c | 43 + kernel/trace/trace.h | 21 + kernel/trace/trace_events.c | 4 + kernel/trace/trace_events_hist.c | 1593 +++++++++++++++++++++++++++++++++++ kernel/trace/trace_events_trigger.c | 29 +- 8 files changed, 1691 insertions(+), 15 deletions(-) create mode 100644 kernel/trace/trace_events_hist.c
include/linux/ftrace_event.h +1 -0
@@ -416,6 +416,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);
kernel/trace/Kconfig +14 -0
@@ -520,6 +520,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
kernel/trace/Makefile +1 -0
@@ -53,6 +53,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_KPROBE_EVENT) += trace_kprobe.o obj-$(CONFIG_TRACEPOINTS) += power-traces.o ifeq ($(CONFIG_PM),y)
kernel/trace/trace.c +43 -0
55 lines not on this page · raw message
kernel/trace/trace.h +21 -0
@@ -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);
@@ -1114,6 +1121,20 @@ struct event_trigger_data {struct list_head list; }; +extern void trigger_data_free (struct event_trigger_data *data); +extern int event_trigger_init (struct event_trigger_ops *ops, +struct event_trigger_data *data); +extern int trace_event_trigger_enable_disable (struct ftrace_event_file *file, +int trigger_enable); +extern void update_cond_flag (struct ftrace_event_file *file); +extern void unregister_trigger (char *glob,struct event_trigger_ops *ops, +struct event_trigger_data *test, +struct ftrace_event_file *file); +extern int set_trigger_filter (char *filter_str, +struct event_trigger_data *trigger_data, +struct ftrace_event_file *file); +extern int register_event_command (struct event_command *cmd); +/** * struct event_trigger_ops - callbacks for trace event triggers *
kernel/trace/trace_events.c +4 -0
@@ -1621,6 +1621,10 @@ event_create_dir(struct dentry *parent, struct ftrace_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);
kernel/trace/trace_events_hist.c new file +1593 -0
1,593 lines not on this page · raw message
kernel/trace/trace_events_trigger.c +14 -15
@@ -28,8 +28,7 @@static LIST_HEAD (trigger_commands);static DEFINE_MUTEX (trigger_cmd_mutex); -static void -trigger_data_free (struct event_trigger_data *data) +void trigger_data_free (struct event_trigger_data *data) {if (data->cmd_ops->set_filter) data->cmd_ops->set_filter (NULL , data,NULL );
@@ -316,7 +315,7 @@ const struct file_operations event_trigger_fops = {* Currently we only register event commands from __init, so mark this * __init too. */ -static __init int register_event_command (struct event_command *cmd) +__init int register_event_command (struct event_command *cmd) {struct event_command *p;int ret =0 ;
@@ -405,9 +404,8 @@ event_trigger_print(const char *name, struct seq_file *m,* * Return: 0 on success, errno otherwise */ -static int -event_trigger_init (struct event_trigger_ops *ops, -struct event_trigger_data *data) +int event_trigger_init (struct event_trigger_ops *ops, +struct event_trigger_data *data) { data->ref++;return 0 ;
@@ -435,8 +433,8 @@ event_trigger_free(struct event_trigger_ops *ops,trigger_data_free (data); } -static int trace_event_trigger_enable_disable (struct ftrace_event_file *file, -int trigger_enable) +int trace_event_trigger_enable_disable (struct ftrace_event_file *file, +int trigger_enable) {int ret =0 ;
@@ -493,7 +491,7 @@ clear_event_triggers(struct trace_array *tr)* its TRIGGER_COND bit set, otherwise the TRIGGER_COND bit should be * cleared. */ -static void update_cond_flag (struct ftrace_event_file *file) +void update_cond_flag (struct ftrace_event_file *file) {struct event_trigger_data *data;bool set_cond = false;
@@ -569,9 +567,9 @@ out:* Usually used directly as the @unreg method in event command * implementations. */ -static void unregister_trigger (char *glob,struct event_trigger_ops *ops, -struct event_trigger_data *test, -struct ftrace_event_file *file) +void unregister_trigger (char *glob,struct event_trigger_ops *ops, +struct event_trigger_data *test, +struct ftrace_event_file *file) {struct event_trigger_data *data;bool unregistered = false;
@@ -705,9 +703,9 @@ event_trigger_callback(struct event_command *cmd_ops,* * Return: 0 on success, errno otherwise */ -static int set_trigger_filter (char *filter_str, -struct event_trigger_data *trigger_data, -struct ftrace_event_file *file) +int set_trigger_filter (char *filter_str, +struct event_trigger_data *trigger_data, +struct ftrace_event_file *file) {struct event_trigger_data *data = trigger_data;struct event_filter *filter =NULL , *tmp;
@@ -1437,6 +1435,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 ; }
Signature · 1 line
1.9.3
v4 cover tracing: 'hist' triggers
This is v4 of the 'hist triggers' patchset, following feedback from v3.
This version 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.
There are a couple of important bits of functionality that were present in v1 but not yet reimplemented in v3.
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:
- 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 e65e0516fb5fdfe1c3138ccd333651739894197f:
Merge branch 'for-next/ftrace/core' into trace/for-next (2015-03-31 10:06:28 -0400)
are available in the git repository at:
git://git.yoctoproject.org/linux-yocto-contrib.git tzanussi/hist-triggers-v4
http://git.yoctoproject.org/cgit/cgit.cgi/linux-yocto-contrib/log/?h=tzanussi/hist-triggers-v4
Tom Zanussi (7):
tracing: Make ftrace_event_field checking 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 'hist' event trigger command
tracing: Add enable_hist/disable_hist triggers
tracing: Add 'hist' trigger Documentation
Documentation/trace/events.txt | 870 ++++++++++++++++++ include/linux/ftrace_event.h | 9 +- kernel/trace/Kconfig | 14 + kernel/trace/Makefile | 1 + kernel/trace/trace.c | 54 ++ kernel/trace/trace.h | 77 +- kernel/trace/trace_events.c | 4 + kernel/trace/trace_events_filter.c | 12 - kernel/trace/trace_events_hist.c | 1708 +++++++++++++++++++++++++++++++++++ kernel/trace/trace_events_trigger.c | 139 +-- kernel/trace/trace_syscalls.c | 11 + 11 files changed, 2819 insertions(+), 80 deletions(-) create mode 100644 kernel/trace/trace_events_hist.c
Signature · 1 line
1.9.3
PATCH v4 6/7 tracing: Add enable_hist/disable_hist triggers
Similar to enable_event/disable_event triggers, these triggers enable and disable the aggregation of events into maps rather than enabling and disabling their writing into the trace buffer.
They can be used to automatically start and stop hist triggers based on a matching filter condition.
If there's a paused hist trigger on system:event, the following would start it when the filter condition was hit:
# echo enable_hist:system:event [ if filter] > event/trigger
And the following would disable a running system:event hist trigger:
# echo disable_hist:system:event [ if filter] > event/trigger
See Documentation/trace/events.txt for real examples.
--- include/linux/ftrace_event.h | 1 + kernel/trace/trace.c | 11 ++++ kernel/trace/trace.h | 32 ++++++++++ kernel/trace/trace_events_hist.c | 115 ++++++++++++++++++++++++++++++++++++ kernel/trace/trace_events_trigger.c | 71 ++++++++++++---------- 5 files changed, 199 insertions(+), 31 deletions(-)
include/linux/ftrace_event.h +1 -0
@@ -417,6 +417,7 @@ enum event_trigger_type {ETT_STACKTRACE = (1 <<2 ),ETT_EVENT_ENABLE = (1 <<3 ),ETT_EVENT_HIST = (1 <<4 ), + ETT_HIST_ENABLE = (1 << 5), };extern int filter_match_preds (struct event_filter *filter,void *rec);
kernel/trace/trace.c +11 -0
@@ -3724,6 +3724,10 @@ static const char readme_msg[] = "\t trigger: traceon, traceoff\n" "\t enable_event:<system>:<event>\n" "\t disable_event:<system>:<event>\n" +#ifdef CONFIG_HIST_TRIGGERS + "\t enable_hist:<system>:<event>\n" + "\t disable_hist:<system>:<event>\n" +#endif#ifdef CONFIG_STACKTRACE "\t\t stacktrace\n"#endif
@@ -3787,6 +3791,13 @@ static const char readme_msg[] ="\t restart a paused hist trigger.\n\n" "\t The 'clear' param will clear the contents of a running hist\n" "\t trigger and leave its current paused/active state.\n\n" +"\t The enable_hist and disable_hist triggers can be used to\n" +"\t have one event conditionally start and stop another event's\n" +"\t already-attached hist trigger. Any number of enable_hist\n" +"\t and disable_hist triggers can be attached to a given event,\n" +"\t allowing that event to kick off and stop aggregations on\n" +"\t a host of other events. See Documentation/trace/events.txt\n" + "\t for examples.\n"#endif ;
kernel/trace/trace.h +32 -0
@@ -1102,8 +1102,10 @@ extern const struct file_operations event_hist_fops;#ifdef CONFIG_HIST_TRIGGERS extern int register_trigger_hist_cmd (void ); +extern int register_trigger_hist_enable_disable_cmds (void );#else static inline int register_trigger_hist_cmd (void ) {return 0 ; } +static inline int register_trigger_hist_enable_disable_cmds (void ) {return 0 ; }#endif extern int register_trigger_cmds (void );
@@ -1121,6 +1123,34 @@ struct event_trigger_data {struct list_head list; }; +/* Avoid typos */ +#define ENABLE_EVENT_STR "enable_event" +#define DISABLE_EVENT_STR "disable_event" +#define ENABLE_HIST_STR "enable_hist" +#define DISABLE_HIST_STR "disable_hist" + +struct enable_trigger_data { +struct ftrace_event_file *file; +bool enable; +bool hist; +}; + +extern int event_enable_trigger_print (struct seq_file *m, +struct event_trigger_ops *ops, +struct event_trigger_data *data); +extern void event_enable_trigger_free (struct event_trigger_ops *ops, +struct event_trigger_data *data); +extern int event_enable_trigger_func (struct event_command *cmd_ops, +struct ftrace_event_file *file, +char *glob,char *cmd,char *param); +extern int event_enable_register_trigger (char *glob, +struct event_trigger_ops *ops, +struct event_trigger_data *data, +struct ftrace_event_file *file); +extern void event_enable_unregister_trigger (char *glob, +struct event_trigger_ops *ops, +struct event_trigger_data *test, +struct ftrace_event_file *file);extern void trigger_data_free (struct event_trigger_data *data);extern int event_trigger_init (struct event_trigger_ops *ops,struct event_trigger_data *data);
@@ -1134,6 +1164,8 @@ extern int set_trigger_filter(char *filter_str,struct event_trigger_data *trigger_data, struct ftrace_event_file *file);extern int register_event_command (struct event_command *cmd); +extern int unregister_event_command (struct event_command *cmd); +extern int register_trigger_hist_enable_disable_cmds (void );/** * struct event_trigger_ops - callbacks for trace event triggers
kernel/trace/trace_events_hist.c +115 -0
118 lines not on this page · raw message
kernel/trace/trace_events_trigger.c +40 -31
161 lines not on this page · raw message
Signature · 1 line
1.9.3
PATCH v4 7/7 tracing: Add 'hist' trigger Documentation
In reply to Tom ZanussiAdd documentation and usage examples for 'hist' triggers.
--- Documentation/trace/events.txt | 870 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 870 insertions(+)
Documentation/trace/events.txt +870 -0
@@ -494,3 +494,873 @@ The following commands are supported: Note that there can be only one traceon or traceoff trigger per triggering event. + +- hist + + This command aggregates event hits into a hash table keyed on a + trace event format fields (or stacktrace) and a set of running + totals derived from one or more trace event format fields and/or + event counts (hitcount). + + The format of a hist trigger is as follows: + + hist:keys=<field1>:values=<field1[,field2,...]> + [:size=#entries][:sort=field1][:pause][:continue] + [:clear] [if <filter>] + + When a matching event is hit, an entry is added to a hash table + using the key(s) and value(s) named. Keys and values correspond to + fields in the event's format description. Values must correspond to + numeric fields - on an event hit, the value(s) will be added to a + sum kept for that field. The special string 'hitcount' can be used + in place of an explicit value field - this is simply a count of + event hits. Keys can be any field, or the special string + 'stacktrace', which will use the event's kernel stacktrace as the + key. The keywords 'keys' or 'key' can be used to specify keys, and + the keyworks 'values', 'vals', or 'val' can be used to specify + values. For the time being, only a single key can be used - + compound keys aren't yet supported. + + 'hist' triggers add a 'hist' file to each event's subdirectory. + Reading the 'hist' file for the event will dump the hash table in + its entirety to stdout. By default, numeric fields are displayed as + base-10 integers. This can be modified by appending any of the + following modifiers to the field name: + + .hex display a number as a hex value + .sym display an address as a symbol + .syscall display a syscall id as a system call name + .execname display a common_pid as a program name + + A typical usage scenario would be the following to enable a hist + trigger, read its current contents, and then turn it off: + + # echo 'hist:keys=skbaddr.hex:vals=len' > \ + /sys/kernel/debug/tracing/events/net/netif_rx/trigger + + # cat /sys/kernel/debug/tracing/events/net/netif_rx/hist + + # echo '!hist:keys=skbaddr.hex:vals=len' > \ + /sys/kernel/debug/tracing/events/net/netif_rx/trigger + + The trigger file itself can be read to show the details of the + currently attached hist trigger. This information is also displayed + at the top of the 'hist' file when read. + + By default, the size of the hash table is 2048 entries. The 'size' + param can be used to specify more or fewer than that. The units are + in terms of hashtable entries - if a run uses more entries than + specified, the results will show the number of 'drops', the number + of hits that were ignored. The size should be a power of 2 between + 128 and 131072 (any non- power-of-2 number specified will be rounded + up). + + The 'sort' param can be used to specify a value field to sort on. + The default if unspecified is 'hitcount' and the default sort order + is 'ascending'. To sort in the opposite direction, append + .descending' to the sort key. + + The 'pause' param can be used to pause an existing hist trigger or + to start a hist trigger but not log any events until told to do so. + 'continue' or 'cont' can be used to start or restart a paused hist + trigger. + + The 'clear' param will clear the contents of a running hist trigger + and leave its current paused/active state. + +- enable_hist/disable_hist + + The enable_hist and disable_hist triggers can be used to have one + event conditionally start and stop another event's already-attached + hist trigger. Any number of enable_hist and disable_hist triggers + can be attached to a given event, allowing that event to kick off + and stop aggregations on a host of other events. + + The format is very similar to the enable/disable_event triggers: + + enable_hist:<system>:<event>[:count] + disable_hist:<system>:<event>[:count] + + Instead of enabling or disabling the tracing of the target event + into the trace buffer as the enable/disable_event triggers do, the + enable/disable_hist triggers enable or disable the aggregation of + the target event into a hash table. + + A typical usage scenario for the enable_hist/disable_hist triggers + would be to first set up a paused hist trigger on some event, + followed by an enable_hist/disable_hist pair that turns the hist + aggregation on and off when conditions of interest are hit: + + # echo 'hist:keys=skbaddr.hex:vals=len:pause' > \ + /sys/kernel/debug/tracing/events/net/netif_receive_skb/trigger + + # echo 'enable_hist:net:netif_receive_skb if filename==/usr/bin/wget' > \ + /sys/kernel/debug/tracing/events/sched/sched_process_exec/trigger + + # echo 'disable_hist:net:netif_receive_skb if comm==wget' > \ + /sys/kernel/debug/tracing/events/sched/sched_process_exit/trigger + + The above sets up an initially paused hist trigger which is unpaused + and starts aggregating events when a given program is executed, and + which stops aggregating when the process exits and the hist trigger + is paused again. + + The examples below provide a more concrete illustration of the + concepts and typical usage patterns discussed above. + + +6.2 'hist' trigger examples +--------------------------- + + The first set of examples creates aggregations using the kmalloc + event. The fields that can be used for the hist trigger are listed + in the kmalloc event's format file: + + # cat /sys/kernel/debug/tracing/events/kmem/kmalloc/format + name: kmalloc + ID: 374 + format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:unsigned long call_site; offset:8; size:8; signed:0; + field:const void * ptr; offset:16; size:8; signed:0; + field:size_t bytes_req; offset:24; size:8; signed:0; + field:size_t bytes_alloc; offset:32; size:8; signed:0; + field:gfp_t gfp_flags; offset:40; size:4; signed:0; + + We'll start by creating a hist trigger that generates a simple table + that lists the total number of bytes requested for each function in + the kernel that made one or more calls to kmalloc: + + # echo 'hist:key=call_site:val=bytes_req' > \ + /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger + + This tells the tracing system to create a 'hist' trigger using the + call_site field of the kmalloc event as the key for the table, which + just means that each unique call_site address will have an entry + created for it in the table. The 'val=bytes_req' parameter tells + the hist trigger that for each unique entry (call_site) in the + table, it should keep a running total of the number of bytes + requested by that call_site. + + We'll let it run for awhile and then dump the contents of the 'hist' + file in the kmalloc event's subdirectory (for readability, a number + of entries have been omitted): + + # cat /sys/kernel/debug/tracing/events/kmem/kmalloc/hist + trigger info: hist:keys=call_site:vals=bytes_req:sort=hitcount:size=2048 [active] + + call_site: 18446744071581750326 hitcount: 1 bytes_req: 24 + call_site: 18446744071583151255 hitcount: 1 bytes_req: 32 + call_site: 18446744071582443167 hitcount: 1 bytes_req: 264 + call_site: 18446744072104099935 hitcount: 2 bytes_req: 464 + call_site: 18446744071579323550 hitcount: 3 bytes_req: 168 + call_site: 18446744071580558850 hitcount: 4 bytes_req: 65536 + . + . + . + call_site: 18446744072101362738 hitcount: 4115 bytes_req: 362120 + call_site: 18446744072103235682 hitcount: 4115 bytes_req: 427960 + call_site: 18446744072102962280 hitcount: 4342 bytes_req: 1637584 + call_site: 18446744072102962328 hitcount: 4973 bytes_req: 360624 + call_site: 18446744072101400062 hitcount: 6465 bytes_req: 258600 + call_site: 18446744071582063959 hitcount: 7818 bytes_req: 15636 + call_site: 18446744072102968908 hitcount: 9315 bytes_req: 8912400 + call_site: 18446744072103122419 hitcount: 9315 bytes_req: 819720 + call_site: 18446744072101402850 hitcount: 10240 bytes_req: 573440 + call_site: 18446744072099471334 hitcount: 23820 bytes_req: 476512 + + Totals: + Hits: 109976 + Entries: 76 + Dropped: 0 + + The output displays a line for each entry, beginning with the key + specified in the trigger, followed by the value(s) also specified in + the trigger. At the beginning of the output is a line that displays + the trigger info, which can also be displayed by reading the + 'trigger' file: + + # cat /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger + hist:keys=call_site:vals=bytes_req:sort=hitcount:size=2048 [active] + + At the end of the output are a few lines that display the overall + totals for the run. The 'Hits' field shows the total number of + times the event trigger was hit, the 'Entries' fields shows the + total number of used entries in the hash table, and the 'Dropped' + field shows the number of hits that were dropped because the number + of used entries for the run exceeded the maximum number of entries + allowed for the table (normally 0, but if not a hint that you may + want to increase the size of the table using the 'size' param). + + Notice in the above output that there's an extra field, 'hitcount', + that wasn't specified in the trigger. Also notice that in the + trigger info a param,'sort=hitcount', which wasn't specified in the + trigger either. The reason is that every trigger implicitly keeps a + count of the total number of hits attributed to a given entry, + called the 'hitcount', and that in the absence of a user-specified + sort param, the hitcount is used as the default sort field. + + The value 'hitcount' can be used in place of an explicit value in + the 'values' param if you don't really need to have any particular + field summed and are mainly interested in hit frequencies. + + To turn the hist trigger off, simply call up the trigger in command + history and re-execute it with a '!' prepended: + + # echo '!hist:key=call_site:val=bytes_req' > \ + /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger + + Finally, notice that the call_site as displayed in the output above + isn't really very useful. It's an address, but normally addresses + are displayed in hex. To have a numeric field displayed as hex + values, simply append '.hex' to the field name in the trigger: + + # echo 'hist:key=call_site.hex:val=bytes_req' > \ + /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger + + # cat /sys/kernel/debug/tracing/events/kmem/kmalloc/hist + trigger info: hist:keys=call_site.hex:vals=bytes_req:sort=hitcount:size=2048 [active] + + call_site: ffffffffa04e85cb hitcount: 1 bytes_req: 511 + call_site: ffffffff810a66a9 hitcount: 1 bytes_req: 1024 + call_site: ffffffff8152db82 hitcount: 1 bytes_req: 8 + call_site: ffffffffa008829e hitcount: 1 bytes_req: 7 + call_site: ffffffffa0087d6a hitcount: 1 bytes_req: 7 + call_site: ffffffffa02eb7e1 hitcount: 1 bytes_req: 433 + call_site: ffffffff8152cbde hitcount: 1 bytes_req: 192 + call_site: ffffffff811a2602 hitcount: 2 bytes_req: 32768 + . + . + . + call_site: ffffffffa0419062 hitcount: 724 bytes_req: 75296 + call_site: ffffffffa024fc32 hitcount: 724 bytes_req: 63712 + call_site: ffffffffa03d6468 hitcount: 852 bytes_req: 416736 + call_site: ffffffffa03d6498 hitcount: 944 bytes_req: 70984 + call_site: ffffffff81311d57 hitcount: 961 bytes_req: 1922 + call_site: ffffffffa0258dfe hitcount: 1429 bytes_req: 57160 + call_site: ffffffffa03fd5f3 hitcount: 1796 bytes_req: 158048 + call_site: ffffffffa03d7e4c hitcount: 1796 bytes_req: 2206848 + call_site: ffffffffa02598e2 hitcount: 3196 bytes_req: 178976 + call_site: ffffffffa0081fe6 hitcount: 9529 bytes_req: 190584 + + Totals: + Hits: 24542 + Entries: 60 + Dropped: 0 + + Even that's only marginally more useful - while hex values do look + more like addresses, what users are typically more interested in + when looking at text addresses are the corresponding symbols + instead. To have an address displayed as symbolic value instead, + simply append '.sym' to the field name in the trigger: + + # echo 'hist:key=call_site.sym:val=bytes_req' > \ + /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger + + # cat /sys/kernel/debug/tracing/events/kmem/kmalloc/hist + trigger info: hist:keys=call_site.sym:vals=bytes_req:sort=hitcount:size=2048 [active] + + call_site: [ffffffff8152db82] usb_control_msg hitcount: 1 bytes_req: 8 + call_site: [ffffffffa04e85cb] __ieee80211_start_scan hitcount: 1 bytes_req: 511 + call_site: [ffffffff815d7f6b] sk_prot_alloc hitcount: 1 bytes_req: 976 + call_site: [ffffffff8152cbde] usb_alloc_urb hitcount: 1 bytes_req: 192 + call_site: [ffffffff815d2c1b] sock_alloc_inode hitcount: 1 bytes_req: 64 + call_site: [ffffffffa02eb7e1] nl80211_trigger_scan hitcount: 1 bytes_req: 433 + call_site: [ffffffffa0087d6a] hidraw_report_event hitcount: 1 bytes_req: 7 + call_site: [ffffffff810a66a9] syslog_print_all hitcount: 1 bytes_req: 1024 + call_site: [ffffffffa008829e] hidraw_send_report hitcount: 1 bytes_req: 7 + call_site: [ffffffff811f1316] mounts_open_common hitcount: 2 bytes_req: 368 + call_site: [ffffffff811d0825] alloc_fdtable hitcount: 2 bytes_req: 96 + call_site: [ffffffffa042106f] __intel_framebuffer_create hitcount: 3 bytes_req: 432 + . + . + . + call_site: [ffffffff8136b079] sg_kmalloc hitcount: 461 bytes_req: 153696 + call_site: [ffffffffa03dbc5e] i915_gem_obj_lookup_or_create_vma hitcount: 462 bytes_req: 92400 + call_site: [ffffffffa03e213a] i915_gem_object_get_pages_gtt hitcount: 462 bytes_req: 7392 + call_site: [ffffffffa03d6498] i915_gem_do_execbuffer.isra.22 hitcount: 697 bytes_req: 52064 + call_site: [ffffffff81311d57] apparmor_file_alloc_security hitcount: 926 bytes_req: 1852 + call_site: [ffffffffa0258dfe] drm_vma_node_allow hitcount: 1050 bytes_req: 42000 + call_site: [ffffffffa03fd5f3] intel_ring_begin hitcount: 1116 bytes_req: 98208 + call_site: [ffffffffa03d7e4c] i915_gem_execbuffer2 hitcount: 1116 bytes_req: 956984 + call_site: [ffffffffa02598e2] drm_modeset_lock_crtc hitcount: 2055 bytes_req: 115080 + call_site: [ffffffffa0081fe6] hid_report_raw_event hitcount: 6433 bytes_req: 128664 + + Totals: + Hits: 17384 + Entries: 62 + Dropped: 0 + + Because the default sort key above is 'hitcount', the above shows a + the list of call_sites by increasing hitcount, so that at the bottom + we see the functions that made the most kmalloc calls during the + run. If instead we we wanted to see the top kmalloc callers in + terms of the number of bytes requested rather than the number of + calls, and we wanted the top caller to appear at the top, we can use + the 'sort' param, along with the 'descending' modifier: + + # echo 'hist:key=call_site.sym:val=bytes_req:sort=bytes_req.descending' > \ + /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger + + # cat /sys/kernel/debug/tracing/events/kmem/kmalloc/hist + trigger info: hist:keys=call_site.sym:vals=bytes_req:sort=bytes_req:size=2048 [active] + + call_site: [ffffffffa03d7e4c] i915_gem_execbuffer2 hitcount: 682 bytes_req: 766360 + call_site: [ffffffff811d6c3b] seq_buf_alloc hitcount: 46 bytes_req: 186176 + call_site: [ffffffffa03d6468] i915_gem_do_execbuffer.isra.22 hitcount: 377 bytes_req: 142808 + call_site: [ffffffffa02598e2] drm_modeset_lock_crtc hitcount: 1107 bytes_req: 61992 + call_site: [ffffffffa03fd5f3] intel_ring_begin hitcount: 682 bytes_req: 60016 + call_site: [ffffffffa0081fe6] hid_report_raw_event hitcount: 2993 bytes_req: 59864 + call_site: [ffffffff8136b079] sg_kmalloc hitcount: 206 bytes_req: 48096 + call_site: [ffffffffa03dbc5e] i915_gem_obj_lookup_or_create_vma hitcount: 206 bytes_req: 41200 + call_site: [ffffffff811a2602] __kmalloc hitcount: 2 bytes_req: 32768 + call_site: [ffffffffa0419062] intel_crtc_page_flip hitcount: 305 bytes_req: 31720 + call_site: [ffffffffa024fc32] drm_mode_page_flip_ioctl hitcount: 305 bytes_req: 26840 + call_site: [ffffffffa0258dfe] drm_vma_node_allow hitcount: 603 bytes_req: 24120 + call_site: [ffffffffa03d6498] i915_gem_do_execbuffer.isra.22 hitcount: 305 bytes_req: 23072 + call_site: [ffffffff812775da] ext4_find_extent hitcount: 79 bytes_req: 7584 + . + . + . + call_site: [ffffffff811f3b07] inotify_handle_event hitcount: 2 bytes_req: 96 + call_site: [ffffffff811bfb6e] vfs_rename hitcount: 6 bytes_req: 66 + call_site: [ffffffff815d2c1b] sock_alloc_inode hitcount: 1 bytes_req: 64 + call_site: [ffffffff811d0825] alloc_fdtable hitcount: 1 bytes_req: 48 + call_site: [ffffffff81220376] proc_self_follow_link hitcount: 2 bytes_req: 24 + call_site: [ffffffff8112e1ec] hist_show hitcount: 2 bytes_req: 16 + call_site: [ffffffff8112e1d0] hist_show hitcount: 2 bytes_req: 16 + call_site: [ffffffff8152db82] usb_control_msg hitcount: 1 bytes_req: 8 + call_site: [ffffffffa008829e] hidraw_send_report hitcount: 1 bytes_req: 7 + call_site: [ffffffffa0087d6a] hidraw_report_event hitcount: 1 bytes_req: 7 + + Totals: + Hits: 9331 + Entries: 57 + Dropped: 0 + + We can also add multiple fields to the 'values' param. For example, + we might want to see the total number of bytes allocated alongside + bytes requested, and display the result sorted by bytes allocated in + a descending order: + + # echo 'hist:keys=call_site.sym:values=bytes_req,bytes_alloc:sort=bytes_alloc.descending' > \ + /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger + + # cat /sys/kernel/debug/tracing/events/kmem/kmalloc/hist + trigger info: hist:keys=call_site.sym:vals=bytes_req,bytes_alloc:sort=bytes_alloc:size=2048 [active] + + call_site: [ffffffffa03d7e4c] i915_gem_execbuffer2 hitcount: 4427 bytes_req: 4190032 bytes_alloc: 5967744 + call_site: [ffffffff811d6c3b] seq_buf_alloc hitcount: 427 bytes_req: 1706768 bytes_alloc: 1765376 + call_site: [ffffffffa03d6468] i915_gem_do_execbuffer.isra.22 hitcount: 1978 bytes_req: 765936 bytes_alloc: 1434304 + call_site: [ffffffffa03fd5f3] intel_ring_begin hitcount: 4427 bytes_req: 389576 bytes_alloc: 424992 + call_site: [ffffffff8136b079] sg_kmalloc hitcount: 993 bytes_req: 305824 bytes_alloc: 376576 + call_site: [ffffffffa02598e2] drm_modeset_lock_crtc hitcount: 4319 bytes_req: 241864 bytes_alloc: 276416 + call_site: [ffffffffa03dbc5e] i915_gem_obj_lookup_or_create_vma hitcount: 993 bytes_req: 198600 bytes_alloc: 254208 + call_site: [ffffffffa0419062] intel_crtc_page_flip hitcount: 1806 bytes_req: 187824 bytes_alloc: 231168 + call_site: [ffffffff81424d78] __tty_buffer_request_room hitcount: 61 bytes_req: 128928 bytes_alloc: 229376 + call_site: [ffffffffa0081fe6] hid_report_raw_event hitcount: 9550 bytes_req: 191024 bytes_alloc: 210160 + call_site: [ffffffffa03d6498] i915_gem_do_execbuffer.isra.22 hitcount: 2449 bytes_req: 184720 bytes_alloc: 209216 + call_site: [ffffffffa0258dfe] drm_vma_node_allow hitcount: 3256 bytes_req: 130240 bytes_alloc: 208384 + . + . + . + call_site: [ffffffffa04e85cb] __ieee80211_start_scan hitcount: 3 bytes_req: 1533 bytes_alloc: 1536 + call_site: [ffffffffa02eb7e1] nl80211_trigger_scan hitcount: 3 bytes_req: 1299 bytes_alloc: 1536 + call_site: [ffffffff8152cbde] usb_alloc_urb hitcount: 6 bytes_req: 1152 bytes_alloc: 1152 + call_site: [ffffffff811f3b07] inotify_handle_event hitcount: 14 bytes_req: 744 bytes_alloc: 896 + call_site: [ffffffff81206b81] load_elf_binary hitcount: 24 bytes_req: 672 bytes_alloc: 768 + call_site: [ffffffff811bfb6e] vfs_rename hitcount: 38 bytes_req: 436 bytes_alloc: 640 + call_site: [ffffffff81220376] proc_self_follow_link hitcount: 24 bytes_req: 288 bytes_alloc: 384 + call_site: [ffffffff815d2c1b] sock_alloc_inode hitcount: 5 bytes_req: 320 bytes_alloc: 320 + call_site: [ffffffff811d0825] alloc_fdtable hitcount: 4 bytes_req: 192 bytes_alloc: 256 + call_site: [ffffffffa04ec05f] ieee80211_start_tx_ba_session hitcount: 1 bytes_req: 232 bytes_alloc: 256 + call_site: [ffffffffa0244701] drm_vm_open_locked hitcount: 5 bytes_req: 160 bytes_alloc: 160 + call_site: [ffffffff8112e1d0] hist_show hitcount: 10 bytes_req: 80 bytes_alloc: 80 + call_site: [ffffffff8112e1ec] hist_show hitcount: 10 bytes_req: 80 bytes_alloc: 80 + call_site: [ffffffff81074c9e] kthread_create_on_node hitcount: 1 bytes_req: 56 bytes_alloc: 64 + call_site: [ffffffff8152db82] usb_control_msg hitcount: 6 bytes_req: 48 bytes_alloc: 48 + call_site: [ffffffffa008829e] hidraw_send_report hitcount: 6 bytes_req: 42 bytes_alloc: 48 + call_site: [ffffffffa0087d6a] hidraw_report_event hitcount: 6 bytes_req: 42 bytes_alloc: 48 + + Totals: + Hits: 45576 + Entries: 66 + Dropped: 0 + + Finally, to finish off our kmalloc example, instead of simply having + the hist trigger display symbolic call_sites, we can have the hist + trigger additionally display the complete set of kernel stack traces + that led to each call_sites. To do that, we simply use the special + value 'stacktrace' for the key param: + + # echo 'hist:keys=stacktrace:values=bytes_req,bytes_alloc:sort=bytes_alloc' > \ + /sys/kernel/debug/tracing/events/kmem/kmalloc/trigger + + The above trigger will use the kernel stack trace in effect when an + event is triggered as the key for the hash table. This allows the + enumeration of every kernel callpath that led up to a particular + event, along with a running total of any of the event fields for + that event. Here we tally bytes requested and bytes allocated for + every callpath in the system that led up to a kmalloc (in this case + every callpath to a kmalloc for a kernel compile): + + # cat /sys/kernel/debug/tracing/events/kmem/kmalloc/hist + trigger info: hist:keys=stacktrace:vals=bytes_req,bytes_alloc:sort=bytes_alloc:size=2048 [active] + + stacktrace: + __kmalloc_track_caller+0x10b/0x190 + kstrdup+0x35/0x70 + __kernfs_new_node+0x34/0xf0 + kernfs_new_node+0x26/0x50 + kernfs_create_dir_ns+0x29/0x80 + sysfs_create_dir_ns+0x40/0xa0 + kobject_add_internal+0xbd/0x3a0 + kobject_add+0x60/0xb0 + device_add+0xff/0x5a0 + device_register+0x1e/0x30 + usb_create_ep_devs+0x81/0xd0 + usb_new_device+0x357/0x410 + hub_event+0xd68/0x11a0 + process_one_work+0x149/0x3d0 + worker_thread+0x121/0x4a0 + kthread+0xd2/0xf0 + hitcount: 1 bytes_req: 6 bytes_alloc: 8 + stacktrace: + kmem_cache_alloc_trace+0xfb/0x160 + usb_control_msg+0x42/0x110 + hub_port_status+0x84/0x120 + hub_port_reset+0x263/0x430 + hub_port_init+0x77/0xbb0 + hub_event+0x944/0x11a0 + process_one_work+0x149/0x3d0 + worker_thread+0x121/0x4a0 + kthread+0xd2/0xf0 + ret_from_fork+0x7c/0xb0 + hitcount: 1 bytes_req: 8 bytes_alloc: 8 + . + . + . + stacktrace: + __kmalloc+0x11b/0x1a0 + load_elf_phdrs+0x76/0xa0 + load_elf_binary+0x102/0x1780 + search_binary_handler+0x97/0x1d0 + do_execveat_common.isra.32+0x551/0x6e0 + SyS_execve+0x3a/0x50 + stub_execve+0x69/0xa0 + hitcount: 131791 bytes_req: 67822944 bytes_alloc: 80279552 + stacktrace: + __kmalloc+0x11b/0x1a0 + i915_gem_execbuffer2+0x6c/0x2c0 [i915] + drm_ioctl+0x1a4/0x630 [drm] + do_vfs_ioctl+0x2f0/0x4f0 + SyS_ioctl+0x81/0xa0 + system_call_fastpath+0x12/0x17 + hitcount: 164540 bytes_req: 134464456 bytes_alloc: 167693376 + stacktrace: + kmem_cache_alloc_trace+0xfb/0x160 + apparmor_file_alloc_security+0x27/0x40 + security_file_alloc+0x16/0x20 + get_empty_filp+0x93/0x1c0 + path_openat+0x31/0x620 + do_filp_open+0x3a/0x90 + do_sys_open+0x128/0x220 + SyS_open+0x1e/0x20 + system_call_fastpath+0x12/0x17 + hitcount: 22118413 bytes_req: 44236888 bytes_alloc: 176947328 + stacktrace: + __kmalloc+0x11b/0x1a0 + seq_buf_alloc+0x1b/0x50 + seq_read+0x2cc/0x370 + proc_reg_read+0x3d/0x80 + __vfs_read+0x18/0x50 + vfs_read+0x86/0x140 + SyS_read+0x46/0xb0 + system_call_fastpath+0x12/0x17 + hitcount: 66168 bytes_req: 271015944 bytes_alloc: 271015952 + + Totals: + Hits: 26780801 + Entries: 633 + Dropped: 0 + + If you key a hist trigger on pid, for example, to gather and display + sorted totals for each process, you can use the special .execname + modifier to display the executable names for the processes in the + table rather than raw pids. The example below keeps a per-process + sum of total bytes read: + + # echo 'hist:key=common_pid.execname:val=count:sort=count.descending' > \ + /sys/kernel/debug/tracing/events/syscalls/sys_enter_read/trigger + + # cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_read/hist + trigger info: hist:keys=common_pid.execname:vals=count:sort=count:size=2048 [active] + + common_pid: Xorg [18446612132314219843] hitcount: 166 count: 159744 + common_pid: gnome-terminal [18446612132314221635] hitcount: 44 count: 113321 + common_pid: compiz [18446612132314221491] hitcount: 13 count: 81968 + common_pid: bash [18446612132314231931] hitcount: 3 count: 66369 + common_pid: gdbus [18446612132314221135] hitcount: 91 count: 1456 + common_pid: acpid [18446612132314219701] hitcount: 48 count: 1152 + common_pid: ibus-daemon [18446612132314221129] hitcount: 71 count: 1136 + common_pid: gdbus [18446612132314221638] hitcount: 42 count: 672 + common_pid: gdbus [18446612132314221345] hitcount: 42 count: 672 + common_pid: ibus-engine-sim [18446612132314221344] hitcount: 14 count: 224 + common_pid: gdbus [18446612132314221262] hitcount: 12 count: 192 + common_pid: gdbus [18446612132314221249] hitcount: 6 count: 96 + common_pid: ibus-ui-gtk3 [18446612132314221245] hitcount: 6 count: 96 + common_pid: gdbus [18446612132314221504] hitcount: 5 count: 80 + common_pid: unity-panel-ser [18446612132314221243] hitcount: 3 count: 48 + common_pid: postgres [18446612132314220339] hitcount: 1 count: 16 + common_pid: gdbus [18446612132314221395] hitcount: 1 count: 16 + common_pid: rtkit-daemon [18446612132314220509] hitcount: 1 count: 8 + common_pid: upstart-dbus-br [18446612132314221100] hitcount: 4 count: 4 + common_pid: bash [18446612132314221924] hitcount: 4 count: 4 + common_pid: firefox [18446612132314222258] hitcount: 1 count: 1 + + Totals: + Hits: 578 + Entries: 21 + Dropped: 0 + + + Similarly, if you key a hist trigger on syscall id, for example to + gather and display a list of systemwide syscall hits, you can use + the special .syscall modifier to display the syscall names rather + than raw ids. The example below keeps a running total of syscall + counts for the system during the run: + + # echo 'hist:key=id.syscall:val=hitcount' > \ + /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/trigger + + # cat /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/hist + trigger info: hist:keys=id.syscall:vals=:sort=hitcount:size=2048 [active] + + id: sys_fadvise64 hitcount: 1 + id: sys_bind hitcount: 1 + id: sys_fsync hitcount: 1 + id: sys_getsockname hitcount: 1 + id: sys_readlink hitcount: 3 + id: sys_set_tid_address hitcount: 4 + . + . + . + id: sys_mprotect hitcount: 57 + id: sys_waitid hitcount: 64 + id: sys_rt_sigaction hitcount: 121 + id: sys_inotify_add_watch hitcount: 234 + id: sys_mmap hitcount: 258 + id: sys_nanosleep hitcount: 310 + id: sys_close hitcount: 323 + id: sys_rt_sigprocmask hitcount: 3520 + id: sys_futex hitcount: 4819 + id: sys_write hitcount: 8907 + id: sys_setitimer hitcount: 39039 + id: sys_writev hitcount: 64528 + id: sys_poll hitcount: 77493 + id: sys_recvmsg hitcount: 168240 + id: sys_ioctl hitcount: 448946 + + Totals: + Hits: 869971 + Entries: 75 + Dropped: 0 + + The next example uses a string field as the hash key and + demonstrates how you can manually pause and continue a hist trigger. + In this example, we'll aggregate fork counts and don't expect a + large number of entries in the hash table, so we'll drop it to a + much smaller number, say 256: + + # echo 'hist:key=child_comm:val=hitcount:size=256' > \ + /sys/kernel/debug/tracing/events/sched/sched_process_fork/trigger + + # cat /sys/kernel/debug/tracing/events/sched/sched_process_fork/hist + trigger info: hist:keys=child_comm:vals=:sort=hitcount:size=256 [active] + + child_comm: gdbus hitcount: 1 + child_comm: smbd hitcount: 1 + child_comm: ibus-daemon hitcount: 1 + child_comm: Cache2 I/O hitcount: 1 + child_comm: dconf worker hitcount: 1 + child_comm: postgres hitcount: 1 + child_comm: unity-panel-ser hitcount: 2 + child_comm: compiz hitcount: 3 + child_comm: bash hitcount: 3 + child_comm: firefox hitcount: 60 + + Totals: + Hits: 74 + Entries: 10 + Dropped: 0 + + # echo 'hist:key=child_comm:val=hitcount:size=256:pause' > \ + /sys/kernel/debug/tracing/events/sched/sched_process_fork/trigger + + If we want to pause the hist trigger, we can simply append :pause to + the command that started the trigger. Notice that the trigger info + displays as [paused]: + + # cat /sys/kernel/debug/tracing/events/sched/sched_process_fork/hist + trigger info: hist:keys=child_comm:vals=:sort=hitcount:size=256 [paused] + + child_comm: dconf worker hitcount: 1 + child_comm: gdbus hitcount: 1 + child_comm: Cache2 I/O hitcount: 1 + child_comm: ibus-daemon hitcount: 1 + child_comm: unity-panel-ser hitcount: 2 + child_comm: smbd hitcount: 2 + child_comm: postgres hitcount: 3 + child_comm: compiz hitcount: 3 + child_comm: bash hitcount: 3 + child_comm: emacs hitcount: 8 + child_comm: firefox hitcount: 60 + + Totals: + Hits: 85 + Entries: 11 + Dropped: 0 + + To manually continue having the trigger aggregate events, append + :cont instead. Notice that the trigger info displays as [active] + again, and the data has changed: + + # echo 'hist:key=child_comm:val=hitcount:size=256:cont' > + /sys/kernel/debug/tracing/events/sched/sched_process_fork/trigger + + # cat /sys/kernel/debug/tracing/events/sched/sched_process_fork/hist + trigger info: hist:keys=child_comm:vals=:sort=hitcount:size=256 [active] + + child_comm: Cache2 I/O hitcount: 1 + child_comm: ibus-daemon hitcount: 1 + child_comm: kthreadd hitcount: 1 + child_comm: gdbus hitcount: 1 + child_comm: dconf worker hitcount: 1 + child_comm: unity-panel-ser hitcount: 2 + child_comm: smbd hitcount: 2 + child_comm: compiz hitcount: 3 + child_comm: postgres hitcount: 4 + child_comm: bash hitcount: 4 + child_comm: emacs hitcount: 8 + child_comm: firefox hitcount: 60 + + Totals: + Hits: 88 + Entries: 12 + Dropped: 0 + + The previous example showed how to start and stop a hist trigger by + appending 'pause' and 'continue' to the hist trigger command. A + hist trigger can also be started in a paused state by initially + starting the trigger with ':pause' appended. This allows you to + start the trigger only when you're ready to start collecting data + and not before. For example, start the trigger in a paused state, + then unpause it and do something you want to measure, then pause the + trigger when done. + + Of course, doing this manually can be difficult and error-prone, but + it is possible to automatically start and stop a hist trigger based + on some condition, via the enable_hist and disable_hist triggers. + + For example, suppose we wanted to take a look at the relative + weights in terms of skb length for each callpath that leads to a + netif_receieve_skb event when downloading a decent-sized file using + wget. + + First we set up an initially paused stacktrace trigger on the + netif_receive_skb event: + + # echo 'hist:key=stacktrace:vals=len:pause' > \ + /sys/kernel/debug/tracing/events/net/netif_receive_skb/trigger + + Next, we set up an 'enable_hist' trigger on the sched_process_exec + event, with an 'if filename==/usr/bin/wget' filter. The effect of + this new trigger is that it will 'unpause' the hist trigger we just + set up on netif_receive_skb if and only if it sees a + sched_process_exec event with a filename of '/usr/bin/wget'. When + that happens, all netif_receive_skb events are aggregated into a + hash table keyed on stacktrace: + + # echo 'enable_hist:net:netif_receive_skb if filename==/usr/bin/wget' > \ + /sys/kernel/debug/tracing/events/sched/sched_process_exec/trigger + + The aggregation continues until the netif_receive_skb is paused + again, which is what the following disable_hist event does by + creating a similar setup on the sched_process_exit event, using the + filter 'comm==wget': + + # echo 'disable_hist:net:netif_receive_skb if comm==wget' > \ + /sys/kernel/debug/tracing/events/sched/sched_process_exit/trigger + + Whenever a process exits and the comm field of the disable_hist + trigger filter matches 'comm==wget', the netif_receive_skb hist + trigger is disabled. + + The overall effect is that netif_received_skb events are aggregated + into the hash table for only the duration of the wget. Executing a + wget command and then listing the 'hist' file will display the + output generated by the wget command: + + $ wget https://www.kernel.org/pub/linux/kernel/v3.x/patch-3.19.xz + + # cat /sys/kernel/debug/tracing/events/net/netif_receive_skb/hist + trigger info: hist:keys=stacktrace:vals=len:sort=hitcount:size=2048 [paused] + + stacktrace: + __netif_receive_skb_core+0x4ad/0x780 + __netif_receive_skb+0x18/0x60 + process_backlog+0xa8/0x150 + net_rx_action+0x15d/0x340 + __do_softirq+0x114/0x2c0 + do_softirq_own_stack+0x1c/0x30 + do_softirq+0x65/0x70 + __local_bh_enable_ip+0xb5/0xc0 + ip_finish_output+0x1f4/0x810 + ip_output+0x68/0xa0 + ip_local_out_sk+0x30/0x40 + ip_send_skb+0x1a/0x50 + udp_send_skb+0x173/0x2a0 + udp_sendmsg+0x2bf/0x9f0 + inet_sendmsg+0x63/0xb0 + do_sock_sendmsg+0x8c/0x100 + hitcount: 4 len: 388 + stacktrace: + __netif_receive_skb_core+0x4ad/0x780 + __netif_receive_skb+0x18/0x60 + netif_receive_skb_internal+0x23/0x90 + napi_gro_receive+0xc8/0x100 + ieee80211_deliver_skb+0xba/0x250 [mac80211] + ieee80211_rx_handlers+0xcc5/0x2240 [mac80211] + ieee80211_prepare_and_rx_handle+0x4e7/0xc30 [mac80211] + ieee80211_rx+0x31d/0x920 [mac80211] + iwlagn_rx_reply_rx+0x3db/0x6f0 [iwldvm] + iwl_rx_dispatch+0x8e/0xf0 [iwldvm] + iwl_pcie_irq_handler+0xe1c/0x12c0 [iwlwifi] + irq_thread_fn+0x20/0x50 + irq_thread+0x11f/0x150 + kthread+0xd2/0xf0 + ret_from_fork+0x7c/0xb0 + hitcount: 35 len: 17415 + stacktrace: + __netif_receive_skb_core+0x4ad/0x780 + __netif_receive_skb+0x18/0x60 + netif_receive_skb_internal+0x23/0x90 + napi_gro_complete+0xa4/0xe0 + dev_gro_receive+0x233/0x360 + napi_gro_receive+0x30/0x100 + ieee80211_deliver_skb+0xba/0x250 [mac80211] + ieee80211_rx_handlers+0xcc5/0x2240 [mac80211] + ieee80211_prepare_and_rx_handle+0x4e7/0xc30 [mac80211] + ieee80211_rx+0x31d/0x920 [mac80211] + iwlagn_rx_reply_rx+0x3db/0x6f0 [iwldvm] + iwl_rx_dispatch+0x8e/0xf0 [iwldvm] + iwl_pcie_irq_handler+0xe1c/0x12c0 [iwlwifi] + irq_thread_fn+0x20/0x50 + irq_thread+0x11f/0x150 + kthread+0xd2/0xf0 + hitcount: 155 len: 636342 + stacktrace: + __netif_receive_skb_core+0x4ad/0x780 + __netif_receive_skb+0x18/0x60 + netif_receive_skb_internal+0x23/0x90 + napi_gro_complete+0xa4/0xe0 + napi_gro_flush+0x6d/0x90 + iwl_pcie_irq_handler+0x90f/0x12c0 [iwlwifi] + irq_thread_fn+0x20/0x50 + irq_thread+0x11f/0x150 + kthread+0xd2/0xf0 + ret_from_fork+0x7c/0xb0 + hitcount: 1013 len: 5531908 + + Totals: + Hits: 1207 + Entries: 4 + Dropped: 0 + + The above shows all the netif_receive_skb callpaths and their total + lengths for the duration of the wget command. + + The 'clear' hist trigger param can be used to clear the hash table. + Suppose we wanted to try another run of the previous example but + this time also wanted to see the complete list of events that went + into the histogram. In order to avoid having to set everything up + again, we can just clear the histogram first: + + # echo 'hist:key=stacktrace:vals=len:clear' > \ + /sys/kernel/debug/tracing/events/net/netif_receive_skb/trigger + + Just to verify that it is in fact cleared, here's what we now see in + the hist file: + + # cat /sys/kernel/debug/tracing/events/net/netif_receive_skb/hist + trigger info: hist:keys=stacktrace:vals=len:sort=hitcount:size=2048 [paused] + + Totals: + Hits: 0 + Entries: 0 + Dropped: 0 + + Since we want to see the detailed list of every netif_receive_skb + event occurring during the new run, which are in fact same events + being aggregated into the hash table, we add some additional + 'enable_event' events the triggering sched_process_exec and + sched_process_exit events as such: + + # echo 'enable_event:net:netif_receive_skb if filename==/usr/bin/wget' > \ + /sys/kernel/debug/tracing/events/sched/sched_process_exec/trigger + + # echo 'disable_event:net:netif_receive_skb if comm==wget' > \ + /sys/kernel/debug/tracing/events/sched/sched_process_exit/trigger + + If you read the trigger files for the sched_process_exec and + sched_process_exit triggers, you should see two triggers for each: + one enabling/disabling the hist aggregation and the other + enabling/disabling the logging of events: + + # cat /sys/kernel/debug/tracing/events/sched/sched_process_exec/trigger + enable_event:net:netif_receive_skb:unlimited if filename==/usr/bin/wget + enable_hist:net:netif_receive_skb:unlimited if filename==/usr/bin/wget + + # cat /sys/kernel/debug/tracing/events/sched/sched_process_exit/trigger + enable_event:net:netif_receive_skb:unlimited if comm==wget + disable_hist:net:netif_receive_skb:unlimited if comm==wget + + In other words, whever either of the sched_process_exec or + sched_process_exit events is hit and matches 'wget', it enables or + disables both the histogram and the event log, and what you end up + with is a hash table and set of events just covering the specified + duration. + + Displaying the 'hist' file should show something similar to what you + saw in the last run, but this time you should also see the + individual events in the trace file: + + # cat /sys/kernel/debug/tracing/trace + # tracer: nop + # + # entries-in-buffer/entries-written: 183/1426 #P:4 + # + # _-----=> irqs-off + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth + # ||| / delay + # TASK-PID CPU# |||| TIMESTAMP FUNCTION + # | | | |||| | | + wget-15108 [000] ..s1 31769.606929: netif_receive_skb: dev=lo skbaddr=ffff88009c353100 len=60 + wget-15108 [000] ..s1 31769.606999: netif_receive_skb: dev=lo skbaddr=ffff88009c353200 len=60 + dnsmasq-1382 [000] ..s1 31769.677652: netif_receive_skb: dev=lo skbaddr=ffff88009c352b00 len=130 + dnsmasq-1382 [000] ..s1 31769.685917: netif_receive_skb: dev=lo skbaddr=ffff88009c352200 len=138 + ##### CPU 2 buffer started #### + irq/29-iwlwifi-559 [002] ..s. 31772.031529: netif_receive_skb: dev=wlan0 skbaddr=ffff88009d433d00 len=2948 + irq/29-iwlwifi-559 [002] ..s. 31772.031572: netif_receive_skb: dev=wlan0 skbaddr=ffff88009d432200 len=1500 + irq/29-iwlwifi-559 [002] ..s. 31772.032196: netif_receive_skb: dev=wlan0 skbaddr=ffff88009d433100 len=2948 + irq/29-iwlwifi-559 [002] ..s. 31772.032761: netif_receive_skb: dev=wlan0 skbaddr=ffff88009d433000 len=2948 + irq/29-iwlwifi-559 [002] ..s. 31772.033220: netif_receive_skb: dev=wlan0 skbaddr=ffff88009d432e00 len=1500 + . + . + .
Signature · 1 line
1.9.3
PATCH v4 1/7 tracing: Make ftrace_event_field checking functions available
In reply to Tom ZanussiMake is_string_field() and is_function_field() accessible outside of trace_event_filters.c for other users of ftrace_event_fields.
--- kernel/trace/trace.h | 12 ++++++++++++ kernel/trace/trace_events_filter.c | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-)
kernel/trace/trace.h +12 -0
@@ -1050,6 +1050,18 @@ struct filter_pred {unsigned short right; }; +static inline bool is_string_field (struct ftrace_event_field *field) +{ +return field->filter_type ==FILTER_DYN_STRING || + field->filter_type ==FILTER_STATIC_STRING || + field->filter_type ==FILTER_PTR_STRING ; +} + +static inline bool is_function_field (struct ftrace_event_field *field) +{ +return field->filter_type ==FILTER_TRACE_FN ; +} +extern enum regex_type filter_parse_regex (char *buff,int len,char **search,int *not); extern void print_event_filter(struct ftrace_event_file *file,
kernel/trace/trace_events_filter.c +0 -12
@@ -917,18 +917,6 @@ int filter_assign_type(const char *type)return FILTER_OTHER ; } -static bool is_function_field (struct ftrace_event_field *field) -{ -return field->filter_type ==FILTER_TRACE_FN ; -} - -static bool is_string_field (struct ftrace_event_field *field) -{ -return field->filter_type ==FILTER_DYN_STRING || - field->filter_type ==FILTER_STATIC_STRING || - field->filter_type ==FILTER_PTR_STRING ; -} -static int is_legal_op (struct ftrace_event_field *field,int op) { if (is_string_field(field) &&
Signature · 1 line
1.9.3
PATCH v4 3/7 tracing: Add get_syscall_name()
In reply to Tom ZanussiAdd a utility function to grab the syscall name from the syscall metadata, given a syscall id.
--- kernel/trace/trace.h | 5 +++++ kernel/trace/trace_syscalls.c | 11 +++++++++++ 2 files changed, 16 insertions(+)
kernel/trace/trace.h +5 -0
@@ -1317,8 +1317,13 @@ int perf_ftrace_event_register(struct ftrace_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
kernel/trace/trace_syscalls.c +11 -0
@@ -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)
Signature · 1 line
1.9.3
Hi Tom,
Quoted 9 lines · Tom Zanussi
This is v4 of the 'hist triggers' patchset, following feedback from v3.
This version 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 intracing_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.
Note: I might be abusing your patch. So this could be a completely bogus question/feedback.
Most important information here is that I placed additional tracepoints during state transitions. I needed to disable the rcu_dereference consistency checks to be able to get it working (not claiming that this is the right thing to do)
include/linux/tracepoint.h +2 -2
@@ -125,7 +125,7 @@ extern void syscall_unregfunc(void);return ; \ prercu; \rcu_read_lock_sched_notrace (); \ - it_func_ptr =rcu_dereference_sched ((tp)->funcs); \ + it_func_ptr =rcu_dereference_raw_notrace ((tp)->funcs); \if (it_func_ptr) { \ do { \ it_func = (it_func_ptr)->func; \
@@ -175,7 +175,7 @@ extern void syscall_unregfunc(void); TP_CONDITION(cond),,); \if (IS_ENABLED (CONFIG_LOCKDEP ) && (cond)) { \rcu_read_lock_sched_notrace (); \ - rcu_dereference_sched(__tracepoint_##name.funcs);\ + rcu_dereference_raw_notrace(__tracepoint_##name.funcs);\rcu_read_unlock_sched_notrace (); \ } \ } \
kernel/trace/trace_irqsoff.c +6 -0
@@ -14,6 +14,8 @@#include <linux/module.h> #include <linux/ftrace.h> +#include <trace/events/sched.h> +#include "trace.h" static struct trace_array *irqsoff_trace__read_mostly ;
@@ -433,11 +435,13 @@ void start_critical_timings(void) {if (preempt_trace () ||irq_trace ())start_critical_timing (CALLER_ADDR0 ,CALLER_ADDR1 ); +trace_sched_latency_critical_timing (1 ); }EXPORT_SYMBOL_GPL (start_critical_timings);void stop_critical_timings (void ) { +trace_sched_latency_critical_timing (0 );if (preempt_trace () ||irq_trace ())stop_critical_timing (CALLER_ADDR0 ,CALLER_ADDR1 ); }
@@ -447,6 +451,7 @@ EXPORT_SYMBOL_GPL(stop_critical_timings);#ifdef CONFIG_PROVE_LOCKING void time_hardirqs_on (unsigned long a0,unsigned long a1) { +trace_sched_latency_irqs (0 );if (!preempt_trace () &&irq_trace ())stop_critical_timing (a0, a1); }
@@ -455,6 +460,7 @@ void time_hardirqs_off(unsigned long a0, unsigned long a1) {if (!preempt_trace () &&irq_trace ())start_critical_timing (a0, a1); +trace_sched_latency_irqs (1 ); }
When activating now the hist trigger by
echo 'hist:key=common_pid:val=enabled' > \
/sys/kernel/debug/tracing/events/sched/sched_latency_preempt/triggerthe system crashes reliable after a very short time. Those tracepoints do work normally, so I see them in /sys/kernel/debug/tracing/trace.
After some investigation I found out that event_hist_trigger() gets called with rec=NULL. This is handed over to hist_field_s32() and that function derefences the argument event (=NULL).
#19 event_hist_trigger (data=0xffff88007934c780, rec=0x0 <irq_stack_union>) at kernel/trace/trace_events_hist.c:885
#20 0xffffffff8113771b in event_triggers_call (file=<optimized out>, rec=0x0 <irq_stack_union>) at kernel/trace/trace_events_trigger.c:78 #21 0xffffffff81079def in ftrace_trigger_soft_disabled (file=<optimized out>) at include/linux/ftrace_event.h:453
#22 ftrace_raw_event_sched_latency_template (__data=0xffff88007c895aa8, enabled=0) at include/trace/events/sched.h:557
#23 0xffffffff8112cbec in trace_sched_latency_preempt (enabled=<optimized out>) at include/trace/events/sched.h:587
#24 trace_preempt_off (a0=18446744071579483412, a1=18446744071579485792) at kernel/trace/trace_irqsoff.c:532
#25 0xffffffff8107f449 in preempt_count_add (val=1) at kernel/sched/core.c:2554
#26 0xffffffff8109bd14 in get_lock_stats (class=0xffffffff82e235d0 <lock_classes+273296>) at kernel/locking/lockdep.c:249
#27 0xffffffff8109c660 in lock_release_holdtime (hlock=0xffff880079c8ee50) at kernel/locking/lockdep.c:267
#28 0xffffffff810a2cc9 in lock_release_holdtime (hlock=<optimized out>) at kernel/locking/lockdep.c:3464
#29 lock_release_nested (ip=<optimized out>, lock=<optimized out>, curr=<optimized out>) at kernel/locking/lockdep.c:3481
#30 __lock_release (ip=<optimized out>, nested=<optimized out>, lock=<optimized out>) at kernel/locking/lockdep.c:3507
#31 lock_release (lock=0xffffffff8226e5f0 <kernfs_mutex+112>, nested=<optimized out>, ip=18446744071590106190) at kernel/locking/lockdep.c:3628 #32 0xffffffff81abd306 in __mutex_unlock_common_slowpath (nested=<optimized out>, lock=<optimized out>) at kernel/locking/mutex.c:744
#33 __mutex_unlock_slowpath (lock_count=0xffff88007934c780) at kernel/locking/mutex.c:769
#34 0xffffffff81abd44e in mutex_unlock (lock=<optimized out>) at kernel/locking/mutex.c:446
#35 0xffffffff81238e84 in kernfs_dop_revalidate (dentry=0xffff88007acede00, flags=<optimized out>) at fs/kernfs/dir.c:470
#36 0xffffffff811c9813 in d_revalidate (flags=<optimized out>, dentry=<optimized out>) at fs/namei.c:607
#37 lookup_fast (nd=0xffff8800790e3e48, path=0xffff8800790e3ca8, inode=0xffff8800790e3cb8) at fs/namei.c:1465
#38 0xffffffff811cc05f in walk_component (follow=<optimized out>, path=<optimized out>, nd=<optimized out>) at fs/namei.c:1577
#39 link_path_walk (name=0xffff880079fa602d "virtual/block/loop6/subsystem", nd=0xffff8800790e3e48) at fs/namei.c:1836
#40 0xffffffff811cd327 in path_init (dfd=<optimized out>, name=0xffff880079fa6020 "/sys/devices/virtual/block/loop6/subsystem", flags=2051493056, nd=0xffff8800790e3e48)
at fs/namei.c:1952
#41 0xffffffff811cda90 in path_lookupat (dfd=<optimized out>, name=<optimized out>, flags=16448, nd=0xffff8800790e3e48) at fs/namei.c:1995 #42 0xffffffff811cec1a in filename_lookup (dfd=-100, name=0xffff880079fa6000, flags=16384, nd=0xffff8800790e3e48) at fs/namei.c:2030 #43 0xffffffff811d0d34 in user_path_at_empty (dfd=-100, name=<optimized out>, flags=16384, path=0xffff8800790e3f38, empty=<optimized out>) at fs/namei.c:2197 #44 0xffffffff811c285c in SYSC_readlinkat (bufsiz=<optimized out>, buf=<optimized out>, pathname=<optimized out>, dfd=<optimized out>) at fs/stat.c:327 #45 SyS_readlinkat (bufsiz=<optimized out>, buf=<optimized out>, pathname=<optimized out>, dfd=<optimized out>) at fs/stat.c:315 #46 SYSC_readlink (bufsiz=<optimized out>, buf=<optimized out>, path=<optimized out>) at fs/stat.c:352
#47 SyS_readlink (path=-131939361831040, buf=0, bufsiz=<optimized out>) at fs/stat.c:349
So I am wondering if the path from ftrace_trigger_soft_disabled() to event_triggers_call() is supposed never to happen? The comment on ftrace_trigger_soft_disabled() indicate this might happen on normal operation:
/**
* ftrace_trigger_soft_disabled - do triggers and test if soft disabled
* @file: The file pointer of the event to test
*
* If any triggers without filters are attached to this event, they
* will be called here. If the event is soft disabled and has no
* triggers that require testing the fields, it will return true,
* otherwise false.
*/
static inline boolftrace_trigger_soft_disabled(struct ftrace_event_file *file)
{
unsigned long eflags = file->flags;
if (!(eflags & FTRACE_EVENT_FL_TRIGGER_COND)) {
if (eflags & FTRACE_EVENT_FL_TRIGGER_MODE)
event_triggers_call(file, NULL);
if (eflags & FTRACE_EVENT_FL_SOFT_DISABLED)
return true;
}
return false;
}cheers,
daniel
Hi Daniel,
I'm traveling and at a conference this week - let me take a look at this next chance I get, but yeah, that ftrace_trigger_soft_disabled() path is a legal path but obviously when called in the case of a NULL rec, things aren't going to work. Probably a bug in my code somewhere - will let you know once I get a chance..
Thanks,
Tom
Quoted 160 lines · Daniel Wagner
Hi Tom,
Quoted 9 lines · Tom Zanussi
This is v4 of the 'hist triggers' patchset, following feedback from v3.
This version 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 intracing_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.Note: I might be abusing your patch. So this could be a completely bogus question/feedback.
Most important information here is that I placed additional tracepoints during state transitions. I needed to disable the
rcu_dereferenceconsistency checks to be able to get it working (not claiming that this is the right thing to do)include/linux/tracepoint.h +2
-2@@ -125,7 +125,7 @@ extern void syscall_unregfunc(void);return ; \ prercu; \rcu_read_lock_sched_notrace (); \ - it_func_ptr =rcu_dereference_sched ((tp)->funcs); \ + it_func_ptr =rcu_dereference_raw_notrace ((tp)->funcs); \if (it_func_ptr) { \ do { \ it_func = (it_func_ptr)->func; \@@ -175,7 +175,7 @@ extern void syscall_unregfunc(void); TP_CONDITION(cond),,); \if (IS_ENABLED (CONFIG_LOCKDEP ) && (cond)) { \rcu_read_lock_sched_notrace (); \ - rcu_dereference_sched(__tracepoint_##name.funcs);\ + rcu_dereference_raw_notrace(__tracepoint_##name.funcs);\rcu_read_unlock_sched_notrace (); \ } \ } \kernel/trace/trace_irqsoff.c +6
-0@@ -14,6 +14,8 @@#include <linux/module.h> #include <linux/ftrace.h> +#include <trace/events/sched.h> +#include "trace.h" static struct trace_array *irqsoff_trace__read_mostly ;@@ -433,11 +435,13 @@ void start_critical_timings(void) {if (preempt_trace () ||irq_trace ())start_critical_timing (CALLER_ADDR0 ,CALLER_ADDR1 ); +trace_sched_latency_critical_timing (1 ); }EXPORT_SYMBOL_GPL (start_critical_timings);void stop_critical_timings (void ) { +trace_sched_latency_critical_timing (0 );if (preempt_trace () ||irq_trace ())stop_critical_timing (CALLER_ADDR0 ,CALLER_ADDR1 ); }@@ -447,6 +451,7 @@ EXPORT_SYMBOL_GPL(stop_critical_timings);#ifdef CONFIG_PROVE_LOCKING void time_hardirqs_on (unsigned long a0,unsigned long a1) { +trace_sched_latency_irqs (0 );if (!preempt_trace () &&irq_trace ())stop_critical_timing (a0, a1); }@@ -455,6 +460,7 @@ void time_hardirqs_off(unsigned long a0, unsigned long a1) {if (!preempt_trace () &&irq_trace ())start_critical_timing (a0, a1); +trace_sched_latency_irqs (1 ); }When activating now the hist trigger by
echo 'hist:key=common_pid:val=enabled' > \ /sys/kernel/debug/tracing/events/sched/sched_latency_preempt/triggerthe system crashes reliable after a very short time. Those tracepoints do work normally, so I see them in
/sys/kernel/debug/tracing/trace.After some investigation I found out that
event_hist_trigger()gets called with rec=NULL. This is handed over tohist_field_s32()and that function derefences the argument event (=NULL).#19
event_hist_trigger(data=0xffff88007934c780, rec=0x0 <irq_stack_union>) atkernel/trace/trace_events_hist.c:885
#200xffffffff8113771binevent_triggers_call(file=<optimized out>, rec=0x0 <irq_stack_union>) atkernel/trace/trace_events_trigger.c:78#210xffffffff81079definftrace_trigger_soft_disabled(file=<optimized out>) atinclude/linux/ftrace_event.h:453
#22ftrace_raw_event_sched_latency_template(__data=0xffff88007c895aa8, enabled=0) atinclude/trace/events/sched.h:557
#230xffffffff8112cbecintrace_sched_latency_preempt(enabled=<optimized out>) atinclude/trace/events/sched.h:587
#24trace_preempt_off(a0=18446744071579483412, a1=18446744071579485792) atkernel/trace/trace_irqsoff.c:532
#250xffffffff8107f449inpreempt_count_add(val=1) atkernel/sched/core.c:2554
#260xffffffff8109bd14inget_lock_stats(class=0xffffffff82e235d0 <lock_classes+273296>) atkernel/locking/lockdep.c:249
#270xffffffff8109c660inlock_release_holdtime(hlock=0xffff880079c8ee50) atkernel/locking/lockdep.c:267
#280xffffffff810a2cc9inlock_release_holdtime(hlock=<optimized out>) atkernel/locking/lockdep.c:3464
#29lock_release_nested(ip=<optimized out>, lock=<optimized out>, curr=<optimized out>) atkernel/locking/lockdep.c:3481
#30__lock_release(ip=<optimized out>, nested=<optimized out>, lock=<optimized out>) atkernel/locking/lockdep.c:3507
#31lock_release(lock=0xffffffff8226e5f0 <kernfs_mutex+112>, nested=<optimized out>, ip=18446744071590106190) atkernel/locking/lockdep.c:3628#320xffffffff81abd306in__mutex_unlock_common_slowpath(nested=<optimized out>, lock=<optimized out>) atkernel/locking/mutex.c:744
#33__mutex_unlock_slowpath(lock_count=0xffff88007934c780) atkernel/locking/mutex.c:769
#340xffffffff81abd44einmutex_unlock(lock=<optimized out>) atkernel/locking/mutex.c:446
#350xffffffff81238e84inkernfs_dop_revalidate(dentry=0xffff88007acede00, flags=<optimized out>) atfs/kernfs/dir.c:470
#360xffffffff811c9813ind_revalidate(flags=<optimized out>, dentry=<optimized out>) atfs/namei.c:607
#37lookup_fast(nd=0xffff8800790e3e48, path=0xffff8800790e3ca8, inode=0xffff8800790e3cb8) atfs/namei.c:1465
#380xffffffff811cc05finwalk_component(follow=<optimized out>, path=<optimized out>, nd=<optimized out>) atfs/namei.c:1577
#39link_path_walk(name=0xffff880079fa602d "virtual/block/loop6/subsystem", nd=0xffff8800790e3e48) atfs/namei.c:1836
#400xffffffff811cd327inpath_init(dfd=<optimized out>, name=0xffff880079fa6020 "/sys/devices/virtual/block/loop6/subsystem", flags=2051493056, nd=0xffff8800790e3e48)at fs/namei.c:1952#41
0xffffffff811cda90inpath_lookupat(dfd=<optimized out>, name=<optimized out>, flags=16448, nd=0xffff8800790e3e48) atfs/namei.c:1995#420xffffffff811cec1ainfilename_lookup(dfd=-100, name=0xffff880079fa6000, flags=16384, nd=0xffff8800790e3e48) atfs/namei.c:2030#430xffffffff811d0d34inuser_path_at_empty(dfd=-100, name=<optimized out>, flags=16384, path=0xffff8800790e3f38, empty=<optimized out>) atfs/namei.c:2197#440xffffffff811c285cinSYSC_readlinkat(bufsiz=<optimized out>, buf=<optimized out>, pathname=<optimized out>, dfd=<optimized out>) atfs/stat.c:327#45SyS_readlinkat(bufsiz=<optimized out>, buf=<optimized out>, pathname=<optimized out>, dfd=<optimized out>) atfs/stat.c:315#46SYSC_readlink(bufsiz=<optimized out>, buf=<optimized out>, path=<optimized out>) atfs/stat.c:352
#47SyS_readlink(path=-131939361831040, buf=0, bufsiz=<optimized out>) atfs/stat.c:349So I am wondering if the path from
ftrace_trigger_soft_disabled()toevent_triggers_call()is supposed never to happen? The comment onftrace_trigger_soft_disabled()indicate this might happen on normal operation:/**
*ftrace_trigger_soft_disabled- do triggers and test if soft disabled
* @file: The file pointer of the event to test
*
* If any triggers without filters are attached to this event, they
* will be called here. If the event is soft disabled and has no
* triggers that require testing the fields, it will return true,
* otherwise false.
*/
static inline boolftrace_trigger_soft_disabled(struct ftrace_event_file *file){ unsigned long eflags = file->flags; if (!(eflags & FTRACE_EVENT_FL_TRIGGER_COND)) { if (eflags & FTRACE_EVENT_FL_TRIGGER_MODE) event_triggers_call(file, NULL); if (eflags & FTRACE_EVENT_FL_SOFT_DISABLED) return true; } return false; }cheers,
daniel
Hi Daniel,
Quoted 130 lines · Daniel Wagner
Hi Tom,
Quoted 9 lines · Tom Zanussi
This is v4 of the 'hist triggers' patchset, following feedback from v3.
This version 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 intracing_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.Note: I might be abusing your patch. So this could be a completely bogus question/feedback.
Most important information here is that I placed additional tracepoints during state transitions. I needed to disable the
rcu_dereferenceconsistency checks to be able to get it working (not claiming that this is the right thing to do)include/linux/tracepoint.h +2
-2@@ -125,7 +125,7 @@ extern void syscall_unregfunc(void);return ; \ prercu; \rcu_read_lock_sched_notrace (); \ - it_func_ptr =rcu_dereference_sched ((tp)->funcs); \ + it_func_ptr =rcu_dereference_raw_notrace ((tp)->funcs); \if (it_func_ptr) { \ do { \ it_func = (it_func_ptr)->func; \@@ -175,7 +175,7 @@ extern void syscall_unregfunc(void); TP_CONDITION(cond),,); \if (IS_ENABLED (CONFIG_LOCKDEP ) && (cond)) { \rcu_read_lock_sched_notrace (); \ - rcu_dereference_sched(__tracepoint_##name.funcs);\ + rcu_dereference_raw_notrace(__tracepoint_##name.funcs);\rcu_read_unlock_sched_notrace (); \ } \ } \kernel/trace/trace_irqsoff.c +6
-0@@ -14,6 +14,8 @@#include <linux/module.h> #include <linux/ftrace.h> +#include <trace/events/sched.h> +#include "trace.h" static struct trace_array *irqsoff_trace__read_mostly ;@@ -433,11 +435,13 @@ void start_critical_timings(void) {if (preempt_trace () ||irq_trace ())start_critical_timing (CALLER_ADDR0 ,CALLER_ADDR1 ); +trace_sched_latency_critical_timing (1 ); }EXPORT_SYMBOL_GPL (start_critical_timings);void stop_critical_timings (void ) { +trace_sched_latency_critical_timing (0 );if (preempt_trace () ||irq_trace ())stop_critical_timing (CALLER_ADDR0 ,CALLER_ADDR1 ); }@@ -447,6 +451,7 @@ EXPORT_SYMBOL_GPL(stop_critical_timings);#ifdef CONFIG_PROVE_LOCKING void time_hardirqs_on (unsigned long a0,unsigned long a1) { +trace_sched_latency_irqs (0 );if (!preempt_trace () &&irq_trace ())stop_critical_timing (a0, a1); }@@ -455,6 +460,7 @@ void time_hardirqs_off(unsigned long a0, unsigned long a1) {if (!preempt_trace () &&irq_trace ())start_critical_timing (a0, a1); +trace_sched_latency_irqs (1 ); }When activating now the hist trigger by
echo 'hist:key=common_pid:val=enabled' > \ /sys/kernel/debug/tracing/events/sched/sched_latency_preempt/triggerthe system crashes reliable after a very short time. Those tracepoints do work normally, so I see them in
/sys/kernel/debug/tracing/trace.After some investigation I found out that
event_hist_trigger()gets called with rec=NULL. This is handed over tohist_field_s32()and that function derefences the argument event (=NULL).#19
event_hist_trigger(data=0xffff88007934c780, rec=0x0 <irq_stack_union>) atkernel/trace/trace_events_hist.c:885
#200xffffffff8113771binevent_triggers_call(file=<optimized out>, rec=0x0 <irq_stack_union>) atkernel/trace/trace_events_trigger.c:78#210xffffffff81079definftrace_trigger_soft_disabled(file=<optimized out>) atinclude/linux/ftrace_event.h:453
#22ftrace_raw_event_sched_latency_template(__data=0xffff88007c895aa8, enabled=0) atinclude/trace/events/sched.h:557
#230xffffffff8112cbecintrace_sched_latency_preempt(enabled=<optimized out>) atinclude/trace/events/sched.h:587
#24trace_preempt_off(a0=18446744071579483412, a1=18446744071579485792) atkernel/trace/trace_irqsoff.c:532
#250xffffffff8107f449inpreempt_count_add(val=1) atkernel/sched/core.c:2554
#260xffffffff8109bd14inget_lock_stats(class=0xffffffff82e235d0 <lock_classes+273296>) atkernel/locking/lockdep.c:249
#270xffffffff8109c660inlock_release_holdtime(hlock=0xffff880079c8ee50) atkernel/locking/lockdep.c:267
#280xffffffff810a2cc9inlock_release_holdtime(hlock=<optimized out>) atkernel/locking/lockdep.c:3464
#29lock_release_nested(ip=<optimized out>, lock=<optimized out>, curr=<optimized out>) atkernel/locking/lockdep.c:3481
#30__lock_release(ip=<optimized out>, nested=<optimized out>, lock=<optimized out>) atkernel/locking/lockdep.c:3507
#31lock_release(lock=0xffffffff8226e5f0 <kernfs_mutex+112>, nested=<optimized out>, ip=18446744071590106190) atkernel/locking/lockdep.c:3628#320xffffffff81abd306in__mutex_unlock_common_slowpath(nested=<optimized out>, lock=<optimized out>) atkernel/locking/mutex.c:744
#33__mutex_unlock_slowpath(lock_count=0xffff88007934c780) atkernel/locking/mutex.c:769
#340xffffffff81abd44einmutex_unlock(lock=<optimized out>) atkernel/locking/mutex.c:446
#350xffffffff81238e84inkernfs_dop_revalidate(dentry=0xffff88007acede00, flags=<optimized out>) atfs/kernfs/dir.c:470
#360xffffffff811c9813ind_revalidate(flags=<optimized out>, dentry=<optimized out>) atfs/namei.c:607
#37lookup_fast(nd=0xffff8800790e3e48, path=0xffff8800790e3ca8, inode=0xffff8800790e3cb8) atfs/namei.c:1465
#380xffffffff811cc05finwalk_component(follow=<optimized out>, path=<optimized out>, nd=<optimized out>) atfs/namei.c:1577
#39link_path_walk(name=0xffff880079fa602d "virtual/block/loop6/subsystem", nd=0xffff8800790e3e48) atfs/namei.c:1836
#400xffffffff811cd327inpath_init(dfd=<optimized out>, name=0xffff880079fa6020 "/sys/devices/virtual/block/loop6/subsystem", flags=2051493056, nd=0xffff8800790e3e48)at fs/namei.c:1952#41
0xffffffff811cda90inpath_lookupat(dfd=<optimized out>, name=<optimized out>, flags=16448, nd=0xffff8800790e3e48) atfs/namei.c:1995#420xffffffff811cec1ainfilename_lookup(dfd=-100, name=0xffff880079fa6000, flags=16384, nd=0xffff8800790e3e48) atfs/namei.c:2030#430xffffffff811d0d34inuser_path_at_empty(dfd=-100, name=<optimized out>, flags=16384, path=0xffff8800790e3f38, empty=<optimized out>) atfs/namei.c:2197#440xffffffff811c285cinSYSC_readlinkat(bufsiz=<optimized out>, buf=<optimized out>, pathname=<optimized out>, dfd=<optimized out>) atfs/stat.c:327#45SyS_readlinkat(bufsiz=<optimized out>, buf=<optimized out>, pathname=<optimized out>, dfd=<optimized out>) atfs/stat.c:315#46SYSC_readlink(bufsiz=<optimized out>, buf=<optimized out>, path=<optimized out>) atfs/stat.c:352
#47SyS_readlink(path=-131939361831040, buf=0, bufsiz=<optimized out>) atfs/stat.c:349
Quoted 5 lines
So I am wondering if the path from
ftrace_trigger_soft_disabled()toevent_triggers_call()is supposed never to happen? The comment onftrace_trigger_soft_disabled()indicate this might happen on normal operation:
So I looked at this on the plane and you're right, this is a path that should never be taken in this case, since the hist trigger does set the post_trigger flag and should therefore TRIGGER_COND would be true and that block should never be entered.
However, the code that registers the trigger first enables the event, then sets the cond flag, which would allow this to happen - a trigger expecting data would be passed a NULL rec.
Can you try the patch below and see if it fixes the problem? (untested and I haven't even had a chance to compile-test it..)
Tom
[PATCH] tracing: Update cond flag before enabling or disabling a trigger
Before a trigger is enabled, the cond flag should be set beforehand, otherwise an 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.
--- kernel/trace/trace_events_trigger.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
kernel/trace/trace_events_trigger.c +6 -4
@@ -546,11 +546,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; }
@@ -578,8 +579,8 @@ 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 ; } }
@@ -1324,11 +1325,12 @@ 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; }
@@ -1351,8 +1353,8 @@ 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 ; } }
Signature · 1 line
1.9.3
Quoted 25 lines
/**
*ftrace_trigger_soft_disabled- do triggers and test if soft disabled
* @file: The file pointer of the event to test
*
* If any triggers without filters are attached to this event, they
* will be called here. If the event is soft disabled and has no
* triggers that require testing the fields, it will return true,
* otherwise false.
*/
static inline boolftrace_trigger_soft_disabled(struct ftrace_event_file *file){ unsigned long eflags = file->flags; if (!(eflags & FTRACE_EVENT_FL_TRIGGER_COND)) { if (eflags & FTRACE_EVENT_FL_TRIGGER_MODE) event_triggers_call(file, NULL); if (eflags & FTRACE_EVENT_FL_SOFT_DISABLED) return true; } return false; }cheers,
daniel
Hi Tom,
Quoted 13 lines · Tom Zanussi
Quoted 6 lines · Daniel Wagner
On 04/10/2015 06:05 PM, Tom Zanussi wrote:
So I am wondering if the path fromftrace_trigger_soft_disabled()toevent_triggers_call()is supposed never to happen? The comment onftrace_trigger_soft_disabled()indicate this might happen on normal operation:So I looked at this on the plane and you're right, this is a path that should never be taken in this case, since the hist trigger does set the
post_triggerflag and should thereforeTRIGGER_CONDwould be true and that block should never be entered.
Quoted 6 lines
However, the code that registers the trigger first enables the event, then sets the cond flag, which would allow this to happen - a trigger expecting data would be passed a NULL rec.
Can you try the patch below and see if it fixes the problem? (untested and I haven't even had a chance to compile-test it..)
Thanks for the explanation. With that and your patch I could get it working. There are a couple of calling places missing in your patch. With them it works nicely. Maybe moving update_cond_flag() inside trace_event_trigger_enable_disable() would be an option?
cheers,
daniel
kernel/trace/trace_events_hist.c +2 -1
@@ -1471,11 +1471,12 @@ static int hist_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; }
kernel/trace/trace_events_trigger.c +2 -0
@@ -1264,6 +1264,7 @@ int event_enable_trigger_func(struct event_command *cmd_ops, goto out_free; } +update_cond_flag (file); ret =trace_event_enable_disable (event_enable_file,1 ,1 );if (ret <0 ) goto out_put;
@@ -1286,6 +1287,7 @@ int event_enable_trigger_func(struct event_command *cmd_ops, out_disable:trace_event_enable_disable (event_enable_file,0 ,1 ); out_put: +update_cond_flag (file);module_put (event_enable_file->event_call->mod); out_free:if (cmd_ops->set_filter)
Quoted 69 lines
Tom
[PATCH] tracing: Update cond flag before enabling or disabling a trigger
Before a trigger is enabled, the cond flag should be set beforehand, otherwise an trigger that's expecting to process a trace record (e.g. one with
post_triggerset) 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 <[email protected]>--- kernel/trace/trace_events_trigger.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)kernel/trace/trace_events_trigger.c +6
-4@@ -546,11 +546,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; }@@ -578,8 +579,8 @@ 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 ; } }@@ -1324,11 +1325,12 @@ 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; }@@ -1351,8 +1353,8 @@ 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 ; } }
Quoted 20 lines · Daniel Wagner
Hi Tom,
Quoted 17 lines · Tom Zanussi
Quoted 6 lines · Daniel Wagner
On 04/10/2015 06:05 PM, Tom Zanussi wrote:
So I am wondering if the path fromftrace_trigger_soft_disabled()toevent_triggers_call()is supposed never to happen? The comment onftrace_trigger_soft_disabled()indicate this might happen on normal operation:So I looked at this on the plane and you're right, this is a path that should never be taken in this case, since the hist trigger does set the
post_triggerflag and should thereforeTRIGGER_CONDwould be true and that block should never be entered.However, the code that registers the trigger first enables the event, then sets the cond flag, which would allow this to happen - a trigger expecting data would be passed a NULL rec.
Quoted 8 lines
Quoted 2 lines
Can you try the patch below and see if it fixes the problem? (untested and I haven't even had a chance to compile-test it..)
Thanks for the explanation. With that and your patch I could get it working. There are a couple of calling places missing in your patch. With them it works nicely. Maybe moving
update_cond_flag()insidetrace_event_trigger_enable_disable()would be an option?
Yeah, I guess I missed the most important bit in my haste, but glad it works for you now. I'll write up a proper patch as soon as I can..
Thanks,
Tom
Quoted 115 lines
cheers,
danielkernel/trace/trace_events_hist.c +2
-1@@ -1471,11 +1471,12 @@ static int hist_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; }kernel/trace/trace_events_trigger.c +2
-0@@ -1264,6 +1264,7 @@ int event_enable_trigger_func(struct event_command *cmd_ops, goto out_free; } +update_cond_flag (file); ret =trace_event_enable_disable (event_enable_file,1 ,1 );if (ret <0 ) goto out_put;@@ -1286,6 +1287,7 @@ int event_enable_trigger_func(struct event_command *cmd_ops, out_disable:trace_event_enable_disable (event_enable_file,0 ,1 ); out_put: +update_cond_flag (file);module_put (event_enable_file->event_call->mod); out_free:if (cmd_ops->set_filter)Quoted 73 lines
Tom
[PATCH] tracing: Update cond flag before enabling or disabling a trigger
Before a trigger is enabled, the cond flag should be set beforehand, otherwise an trigger that's expecting to process a trace record (e.g. one with
post_triggerset) 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 <[email protected]>--- kernel/trace/trace_events_trigger.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)kernel/trace/trace_events_trigger.c +6
-4@@ -546,11 +546,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; }@@ -578,8 +579,8 @@ 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 ; } }@@ -1324,11 +1325,12 @@ 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; }@@ -1351,8 +1353,8 @@ 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 ; } }