diff options
author | Steven Rostedt (Red Hat) <rostedt@goodmis.org> | 2013-03-12 11:49:18 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-03-15 00:36:01 -0400 |
commit | 77fd5c15e3216b901be69047ca43b05ae9099951 (patch) | |
tree | 1790b9bacd1d371cf03902efaf99cdc9d08abdec /kernel/trace/trace.c | |
parent | 3209cff4490bee55fd2bc1d087cb8ecf2a686a88 (diff) |
tracing: Add snapshot trigger to function probes
echo 'schedule:snapshot:1' > /debug/tracing/set_ftrace_filter
This will cause the scheduler to trigger a snapshot the next time
it's called (you can use any function that's not called by NMI).
Even though it triggers only once, you still need to remove it with:
echo '!schedule:snapshot:0' > /debug/tracing/set_ftrace_filter
The :1 can be left off for the first command:
echo 'schedule:snapshot' > /debug/tracing/set_ftrace_filter
But this will cause all calls to schedule to trigger a snapshot.
This must be removed without the ':0'
echo '!schedule:snapshot' > /debug/tracing/set_ftrace_filter
As adding a "count" is a different operation (internally).
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace/trace.c')
-rw-r--r-- | kernel/trace/trace.c | 111 |
1 files changed, 110 insertions, 1 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 906049c0af90..c5b844621562 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -5086,7 +5086,114 @@ static const struct file_operations tracing_dyn_info_fops = { | |||
5086 | .read = tracing_read_dyn_info, | 5086 | .read = tracing_read_dyn_info, |
5087 | .llseek = generic_file_llseek, | 5087 | .llseek = generic_file_llseek, |
5088 | }; | 5088 | }; |
5089 | #endif | 5089 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
5090 | |||
5091 | #if defined(CONFIG_TRACER_SNAPSHOT) && defined(CONFIG_DYNAMIC_FTRACE) | ||
5092 | static void | ||
5093 | ftrace_snapshot(unsigned long ip, unsigned long parent_ip, void **data) | ||
5094 | { | ||
5095 | tracing_snapshot(); | ||
5096 | } | ||
5097 | |||
5098 | static void | ||
5099 | ftrace_count_snapshot(unsigned long ip, unsigned long parent_ip, void **data) | ||
5100 | { | ||
5101 | unsigned long *count = (long *)data; | ||
5102 | |||
5103 | if (!*count) | ||
5104 | return; | ||
5105 | |||
5106 | if (*count != -1) | ||
5107 | (*count)--; | ||
5108 | |||
5109 | tracing_snapshot(); | ||
5110 | } | ||
5111 | |||
5112 | static int | ||
5113 | ftrace_snapshot_print(struct seq_file *m, unsigned long ip, | ||
5114 | struct ftrace_probe_ops *ops, void *data) | ||
5115 | { | ||
5116 | long count = (long)data; | ||
5117 | |||
5118 | seq_printf(m, "%ps:", (void *)ip); | ||
5119 | |||
5120 | seq_printf(m, "snapshot"); | ||
5121 | |||
5122 | if (count == -1) | ||
5123 | seq_printf(m, ":unlimited\n"); | ||
5124 | else | ||
5125 | seq_printf(m, ":count=%ld\n", count); | ||
5126 | |||
5127 | return 0; | ||
5128 | } | ||
5129 | |||
5130 | static struct ftrace_probe_ops snapshot_probe_ops = { | ||
5131 | .func = ftrace_snapshot, | ||
5132 | .print = ftrace_snapshot_print, | ||
5133 | }; | ||
5134 | |||
5135 | static struct ftrace_probe_ops snapshot_count_probe_ops = { | ||
5136 | .func = ftrace_count_snapshot, | ||
5137 | .print = ftrace_snapshot_print, | ||
5138 | }; | ||
5139 | |||
5140 | static int | ||
5141 | ftrace_trace_snapshot_callback(struct ftrace_hash *hash, | ||
5142 | char *glob, char *cmd, char *param, int enable) | ||
5143 | { | ||
5144 | struct ftrace_probe_ops *ops; | ||
5145 | void *count = (void *)-1; | ||
5146 | char *number; | ||
5147 | int ret; | ||
5148 | |||
5149 | /* hash funcs only work with set_ftrace_filter */ | ||
5150 | if (!enable) | ||
5151 | return -EINVAL; | ||
5152 | |||
5153 | ops = param ? &snapshot_count_probe_ops : &snapshot_probe_ops; | ||
5154 | |||
5155 | if (glob[0] == '!') { | ||
5156 | unregister_ftrace_function_probe_func(glob+1, ops); | ||
5157 | return 0; | ||
5158 | } | ||
5159 | |||
5160 | if (!param) | ||
5161 | goto out_reg; | ||
5162 | |||
5163 | number = strsep(¶m, ":"); | ||
5164 | |||
5165 | if (!strlen(number)) | ||
5166 | goto out_reg; | ||
5167 | |||
5168 | /* | ||
5169 | * We use the callback data field (which is a pointer) | ||
5170 | * as our counter. | ||
5171 | */ | ||
5172 | ret = kstrtoul(number, 0, (unsigned long *)&count); | ||
5173 | if (ret) | ||
5174 | return ret; | ||
5175 | |||
5176 | out_reg: | ||
5177 | ret = register_ftrace_function_probe(glob, ops, count); | ||
5178 | |||
5179 | if (ret >= 0) | ||
5180 | alloc_snapshot(&global_trace); | ||
5181 | |||
5182 | return ret < 0 ? ret : 0; | ||
5183 | } | ||
5184 | |||
5185 | static struct ftrace_func_command ftrace_snapshot_cmd = { | ||
5186 | .name = "snapshot", | ||
5187 | .func = ftrace_trace_snapshot_callback, | ||
5188 | }; | ||
5189 | |||
5190 | static int register_snapshot_cmd(void) | ||
5191 | { | ||
5192 | return register_ftrace_command(&ftrace_snapshot_cmd); | ||
5193 | } | ||
5194 | #else | ||
5195 | static inline int register_snapshot_cmd(void) { return 0; } | ||
5196 | #endif /* defined(CONFIG_TRACER_SNAPSHOT) && defined(CONFIG_DYNAMIC_FTRACE) */ | ||
5090 | 5197 | ||
5091 | struct dentry *tracing_init_dentry_tr(struct trace_array *tr) | 5198 | struct dentry *tracing_init_dentry_tr(struct trace_array *tr) |
5092 | { | 5199 | { |
@@ -6076,6 +6183,8 @@ __init static int tracer_alloc_buffers(void) | |||
6076 | trace_set_options(&global_trace, option); | 6183 | trace_set_options(&global_trace, option); |
6077 | } | 6184 | } |
6078 | 6185 | ||
6186 | register_snapshot_cmd(); | ||
6187 | |||
6079 | return 0; | 6188 | return 0; |
6080 | 6189 | ||
6081 | out_free_cpumask: | 6190 | out_free_cpumask: |