blob: 483162a9f9080258fa08b76247e40103d8e5a6c5 [file] [log] [blame]
Steven Rostedt1b29b012008-05-12 21:20:42 +02001/*
2 * ring buffer based function tracer
3 *
4 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
5 * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
6 *
7 * Based on code from the latency_tracer, that is:
8 *
9 * Copyright (C) 2004-2006 Ingo Molnar
10 * Copyright (C) 2004 William Lee Irwin III
11 */
Steven Rostedt23b4ff32009-02-14 19:04:24 -050012#include <linux/ring_buffer.h>
Steven Rostedt1b29b012008-05-12 21:20:42 +020013#include <linux/debugfs.h>
14#include <linux/uaccess.h>
15#include <linux/ftrace.h>
Anton Vorontsov21f67942012-07-09 17:10:42 -070016#include <linux/pstore.h>
Ingo Molnar2e0f5762008-05-12 21:20:49 +020017#include <linux/fs.h>
Steven Rostedt1b29b012008-05-12 21:20:42 +020018
19#include "trace.h"
20
Steven Rostedta225cdd2009-01-15 23:06:03 -050021/* function tracing enabled */
22static int ftrace_function_enabled;
23
Steven Rostedt53614992009-01-15 19:12:40 -050024static struct trace_array *func_trace;
25
Steven Rostedta225cdd2009-01-15 23:06:03 -050026static void tracing_start_function_trace(void);
27static void tracing_stop_function_trace(void);
28
Arnaldo Carvalho de Melob6f11df2009-02-05 18:02:00 -020029static int function_trace_init(struct trace_array *tr)
Steven Rostedt1b29b012008-05-12 21:20:42 +020030{
Steven Rostedtbb3c3c92009-01-15 20:40:23 -050031 func_trace = tr;
Steven Rostedt26bc83f2008-07-10 20:58:14 -040032 tr->cpu = get_cpu();
Steven Rostedt26bc83f2008-07-10 20:58:14 -040033 put_cpu();
34
Steven Rostedt41bc8142008-05-22 11:49:22 -040035 tracing_start_cmdline_record();
Steven Rostedt1b29b012008-05-12 21:20:42 +020036 tracing_start_function_trace();
Frederic Weisbecker1c800252008-11-16 05:57:26 +010037 return 0;
Steven Rostedt1b29b012008-05-12 21:20:42 +020038}
39
Ingo Molnare309b412008-05-12 21:20:51 +020040static void function_trace_reset(struct trace_array *tr)
Steven Rostedt1b29b012008-05-12 21:20:42 +020041{
Arnaldo Carvalho de Melob6f11df2009-02-05 18:02:00 -020042 tracing_stop_function_trace();
43 tracing_stop_cmdline_record();
Steven Rostedt1b29b012008-05-12 21:20:42 +020044}
45
Steven Rostedt90369902008-11-05 16:05:44 -050046static void function_trace_start(struct trace_array *tr)
47{
Pekka J Enberg213cc062008-12-19 12:08:39 +020048 tracing_reset_online_cpus(tr);
Steven Rostedt90369902008-11-05 16:05:44 -050049}
50
Steven Rostedt53614992009-01-15 19:12:40 -050051static void
Steven Rostedt2f5f6ad2011-08-08 16:57:47 -040052function_trace_call_preempt_only(unsigned long ip, unsigned long parent_ip,
Steven Rostedta1e2e312011-08-09 12:50:46 -040053 struct ftrace_ops *op, struct pt_regs *pt_regs)
Steven Rostedtbb3c3c92009-01-15 20:40:23 -050054{
55 struct trace_array *tr = func_trace;
56 struct trace_array_cpu *data;
57 unsigned long flags;
58 long disabled;
Steven Rostedt5168ae52010-06-03 09:36:50 -040059 int cpu;
Steven Rostedtbb3c3c92009-01-15 20:40:23 -050060 int pc;
61
62 if (unlikely(!ftrace_function_enabled))
63 return;
64
65 pc = preempt_count();
Steven Rostedt5168ae52010-06-03 09:36:50 -040066 preempt_disable_notrace();
Steven Rostedtbb3c3c92009-01-15 20:40:23 -050067 local_save_flags(flags);
68 cpu = raw_smp_processor_id();
69 data = tr->data[cpu];
70 disabled = atomic_inc_return(&data->disabled);
71
72 if (likely(disabled == 1))
Arnaldo Carvalho de Melo7be42152009-02-05 01:13:37 -050073 trace_function(tr, ip, parent_ip, flags, pc);
Steven Rostedtbb3c3c92009-01-15 20:40:23 -050074
75 atomic_dec(&data->disabled);
Steven Rostedt5168ae52010-06-03 09:36:50 -040076 preempt_enable_notrace();
Steven Rostedtbb3c3c92009-01-15 20:40:23 -050077}
78
Anton Vorontsov21f67942012-07-09 17:10:42 -070079/* Our two options */
80enum {
81 TRACE_FUNC_OPT_STACK = 0x1,
82 TRACE_FUNC_OPT_PSTORE = 0x2,
83};
84
85static struct tracer_flags func_flags;
86
Steven Rostedtbb3c3c92009-01-15 20:40:23 -050087static void
Steven Rostedt2f5f6ad2011-08-08 16:57:47 -040088function_trace_call(unsigned long ip, unsigned long parent_ip,
Steven Rostedta1e2e312011-08-09 12:50:46 -040089 struct ftrace_ops *op, struct pt_regs *pt_regs)
90
Steven Rostedtbb3c3c92009-01-15 20:40:23 -050091{
92 struct trace_array *tr = func_trace;
93 struct trace_array_cpu *data;
94 unsigned long flags;
95 long disabled;
96 int cpu;
97 int pc;
98
99 if (unlikely(!ftrace_function_enabled))
100 return;
101
102 /*
103 * Need to use raw, since this must be called before the
104 * recursive protection is performed.
105 */
106 local_irq_save(flags);
107 cpu = raw_smp_processor_id();
108 data = tr->data[cpu];
109 disabled = atomic_inc_return(&data->disabled);
110
111 if (likely(disabled == 1)) {
Anton Vorontsov21f67942012-07-09 17:10:42 -0700112 /*
113 * So far tracing doesn't support multiple buffers, so
114 * we make an explicit call for now.
115 */
116 if (unlikely(func_flags.val & TRACE_FUNC_OPT_PSTORE))
117 pstore_ftrace_call(ip, parent_ip);
Steven Rostedtbb3c3c92009-01-15 20:40:23 -0500118 pc = preempt_count();
Arnaldo Carvalho de Melo7be42152009-02-05 01:13:37 -0500119 trace_function(tr, ip, parent_ip, flags, pc);
Steven Rostedtbb3c3c92009-01-15 20:40:23 -0500120 }
121
122 atomic_dec(&data->disabled);
123 local_irq_restore(flags);
124}
125
126static void
Steven Rostedt2f5f6ad2011-08-08 16:57:47 -0400127function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
Steven Rostedta1e2e312011-08-09 12:50:46 -0400128 struct ftrace_ops *op, struct pt_regs *pt_regs)
Steven Rostedt53614992009-01-15 19:12:40 -0500129{
130 struct trace_array *tr = func_trace;
131 struct trace_array_cpu *data;
132 unsigned long flags;
133 long disabled;
134 int cpu;
135 int pc;
136
137 if (unlikely(!ftrace_function_enabled))
138 return;
139
140 /*
141 * Need to use raw, since this must be called before the
142 * recursive protection is performed.
143 */
144 local_irq_save(flags);
145 cpu = raw_smp_processor_id();
146 data = tr->data[cpu];
147 disabled = atomic_inc_return(&data->disabled);
148
149 if (likely(disabled == 1)) {
150 pc = preempt_count();
Arnaldo Carvalho de Melo7be42152009-02-05 01:13:37 -0500151 trace_function(tr, ip, parent_ip, flags, pc);
Steven Rostedt53614992009-01-15 19:12:40 -0500152 /*
153 * skip over 5 funcs:
154 * __ftrace_trace_stack,
155 * __trace_stack,
156 * function_stack_trace_call
157 * ftrace_list_func
158 * ftrace_call
159 */
Arnaldo Carvalho de Melo7be42152009-02-05 01:13:37 -0500160 __trace_stack(tr, flags, 5, pc);
Steven Rostedt53614992009-01-15 19:12:40 -0500161 }
162
163 atomic_dec(&data->disabled);
164 local_irq_restore(flags);
165}
166
Steven Rostedtbb3c3c92009-01-15 20:40:23 -0500167
168static struct ftrace_ops trace_ops __read_mostly =
169{
170 .func = function_trace_call,
Steven Rostedt47409742012-07-20 11:04:44 -0400171 .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE,
Steven Rostedtbb3c3c92009-01-15 20:40:23 -0500172};
173
Steven Rostedt53614992009-01-15 19:12:40 -0500174static struct ftrace_ops trace_stack_ops __read_mostly =
175{
176 .func = function_stack_trace_call,
Steven Rostedt47409742012-07-20 11:04:44 -0400177 .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE,
Steven Rostedt53614992009-01-15 19:12:40 -0500178};
179
Steven Rostedt53614992009-01-15 19:12:40 -0500180static struct tracer_opt func_opts[] = {
181#ifdef CONFIG_STACKTRACE
182 { TRACER_OPT(func_stack_trace, TRACE_FUNC_OPT_STACK) },
183#endif
Anton Vorontsov21f67942012-07-09 17:10:42 -0700184#ifdef CONFIG_PSTORE_FTRACE
185 { TRACER_OPT(func_pstore, TRACE_FUNC_OPT_PSTORE) },
186#endif
Steven Rostedt53614992009-01-15 19:12:40 -0500187 { } /* Always set a last empty entry */
188};
189
190static struct tracer_flags func_flags = {
191 .val = 0, /* By default: all flags disabled */
192 .opts = func_opts
193};
194
Steven Rostedta225cdd2009-01-15 23:06:03 -0500195static void tracing_start_function_trace(void)
Steven Rostedt3eb36aa2009-01-15 22:21:43 -0500196{
197 ftrace_function_enabled = 0;
198
199 if (trace_flags & TRACE_ITER_PREEMPTONLY)
200 trace_ops.func = function_trace_call_preempt_only;
201 else
202 trace_ops.func = function_trace_call;
203
204 if (func_flags.val & TRACE_FUNC_OPT_STACK)
205 register_ftrace_function(&trace_stack_ops);
206 else
207 register_ftrace_function(&trace_ops);
208
209 ftrace_function_enabled = 1;
210}
211
Steven Rostedta225cdd2009-01-15 23:06:03 -0500212static void tracing_stop_function_trace(void)
Steven Rostedt3eb36aa2009-01-15 22:21:43 -0500213{
214 ftrace_function_enabled = 0;
Frederic Weisbeckerc85a17e2009-06-20 05:45:14 +0200215
216 if (func_flags.val & TRACE_FUNC_OPT_STACK)
217 unregister_ftrace_function(&trace_stack_ops);
218 else
219 unregister_ftrace_function(&trace_ops);
Steven Rostedt3eb36aa2009-01-15 22:21:43 -0500220}
221
Steven Rostedt53614992009-01-15 19:12:40 -0500222static int func_set_flag(u32 old_flags, u32 bit, int set)
223{
Anton Vorontsovf555f122012-07-09 17:10:46 -0700224 switch (bit) {
225 case TRACE_FUNC_OPT_STACK:
Steven Rostedt53614992009-01-15 19:12:40 -0500226 /* do nothing if already set */
227 if (!!set == !!(func_flags.val & TRACE_FUNC_OPT_STACK))
Anton Vorontsovf555f122012-07-09 17:10:46 -0700228 break;
Steven Rostedt53614992009-01-15 19:12:40 -0500229
Steven Rostedt3eb36aa2009-01-15 22:21:43 -0500230 if (set) {
231 unregister_ftrace_function(&trace_ops);
Steven Rostedt53614992009-01-15 19:12:40 -0500232 register_ftrace_function(&trace_stack_ops);
Steven Rostedt3eb36aa2009-01-15 22:21:43 -0500233 } else {
Steven Rostedt53614992009-01-15 19:12:40 -0500234 unregister_ftrace_function(&trace_stack_ops);
Steven Rostedt3eb36aa2009-01-15 22:21:43 -0500235 register_ftrace_function(&trace_ops);
236 }
Steven Rostedt53614992009-01-15 19:12:40 -0500237
Anton Vorontsovf555f122012-07-09 17:10:46 -0700238 break;
239 case TRACE_FUNC_OPT_PSTORE:
240 break;
241 default:
242 return -EINVAL;
Steven Rostedt53614992009-01-15 19:12:40 -0500243 }
244
Anton Vorontsovf555f122012-07-09 17:10:46 -0700245 return 0;
Steven Rostedt53614992009-01-15 19:12:40 -0500246}
247
Steven Rostedt1b29b012008-05-12 21:20:42 +0200248static struct tracer function_trace __read_mostly =
249{
Steven Rostedt3eb36aa2009-01-15 22:21:43 -0500250 .name = "function",
251 .init = function_trace_init,
252 .reset = function_trace_reset,
253 .start = function_trace_start,
Frederic Weisbecker6eaaa5d2009-02-11 02:25:00 +0100254 .wait_pipe = poll_wait_pipe,
Steven Rostedt53614992009-01-15 19:12:40 -0500255 .flags = &func_flags,
256 .set_flag = func_set_flag,
Steven Rostedt60a11772008-05-12 21:20:44 +0200257#ifdef CONFIG_FTRACE_SELFTEST
Steven Rostedt3eb36aa2009-01-15 22:21:43 -0500258 .selftest = trace_selftest_startup_function,
Steven Rostedt60a11772008-05-12 21:20:44 +0200259#endif
Steven Rostedt1b29b012008-05-12 21:20:42 +0200260};
261
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500262#ifdef CONFIG_DYNAMIC_FTRACE
263static void
264ftrace_traceon(unsigned long ip, unsigned long parent_ip, void **data)
265{
266 long *count = (long *)data;
267
268 if (tracing_is_on())
269 return;
270
271 if (!*count)
272 return;
273
274 if (*count != -1)
275 (*count)--;
276
277 tracing_on();
278}
279
280static void
281ftrace_traceoff(unsigned long ip, unsigned long parent_ip, void **data)
282{
283 long *count = (long *)data;
284
285 if (!tracing_is_on())
286 return;
287
288 if (!*count)
289 return;
290
291 if (*count != -1)
292 (*count)--;
293
294 tracing_off();
295}
296
Steven Rostedte110e3d2009-02-16 23:38:13 -0500297static int
298ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
Steven Rostedtb6887d72009-02-17 12:32:04 -0500299 struct ftrace_probe_ops *ops, void *data);
Steven Rostedte110e3d2009-02-16 23:38:13 -0500300
Steven Rostedtb6887d72009-02-17 12:32:04 -0500301static struct ftrace_probe_ops traceon_probe_ops = {
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500302 .func = ftrace_traceon,
Steven Rostedte110e3d2009-02-16 23:38:13 -0500303 .print = ftrace_trace_onoff_print,
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500304};
305
Steven Rostedtb6887d72009-02-17 12:32:04 -0500306static struct ftrace_probe_ops traceoff_probe_ops = {
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500307 .func = ftrace_traceoff,
Steven Rostedte110e3d2009-02-16 23:38:13 -0500308 .print = ftrace_trace_onoff_print,
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500309};
310
311static int
Steven Rostedte110e3d2009-02-16 23:38:13 -0500312ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
Steven Rostedtb6887d72009-02-17 12:32:04 -0500313 struct ftrace_probe_ops *ops, void *data)
Steven Rostedte110e3d2009-02-16 23:38:13 -0500314{
Steven Rostedte110e3d2009-02-16 23:38:13 -0500315 long count = (long)data;
316
Steven Rostedtb375a112009-09-17 00:05:58 -0400317 seq_printf(m, "%ps:", (void *)ip);
Steven Rostedte110e3d2009-02-16 23:38:13 -0500318
Steven Rostedtb6887d72009-02-17 12:32:04 -0500319 if (ops == &traceon_probe_ops)
Steven Rostedte110e3d2009-02-16 23:38:13 -0500320 seq_printf(m, "traceon");
321 else
322 seq_printf(m, "traceoff");
323
Steven Rostedt35ebf1c2009-02-17 13:12:12 -0500324 if (count == -1)
325 seq_printf(m, ":unlimited\n");
326 else
Li Zefan00e54d02009-06-25 14:05:27 +0800327 seq_printf(m, ":count=%ld\n", count);
Steven Rostedte110e3d2009-02-16 23:38:13 -0500328
329 return 0;
330}
331
332static int
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500333ftrace_trace_onoff_unreg(char *glob, char *cmd, char *param)
334{
Steven Rostedtb6887d72009-02-17 12:32:04 -0500335 struct ftrace_probe_ops *ops;
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500336
337 /* we register both traceon and traceoff to this callback */
338 if (strcmp(cmd, "traceon") == 0)
Steven Rostedtb6887d72009-02-17 12:32:04 -0500339 ops = &traceon_probe_ops;
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500340 else
Steven Rostedtb6887d72009-02-17 12:32:04 -0500341 ops = &traceoff_probe_ops;
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500342
Steven Rostedtb6887d72009-02-17 12:32:04 -0500343 unregister_ftrace_function_probe_func(glob, ops);
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500344
345 return 0;
346}
347
348static int
Steven Rostedt43dd61c2011-07-07 11:09:22 -0400349ftrace_trace_onoff_callback(struct ftrace_hash *hash,
350 char *glob, char *cmd, char *param, int enable)
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500351{
Steven Rostedtb6887d72009-02-17 12:32:04 -0500352 struct ftrace_probe_ops *ops;
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500353 void *count = (void *)-1;
354 char *number;
355 int ret;
356
357 /* hash funcs only work with set_ftrace_filter */
358 if (!enable)
359 return -EINVAL;
360
361 if (glob[0] == '!')
362 return ftrace_trace_onoff_unreg(glob+1, cmd, param);
363
364 /* we register both traceon and traceoff to this callback */
365 if (strcmp(cmd, "traceon") == 0)
Steven Rostedtb6887d72009-02-17 12:32:04 -0500366 ops = &traceon_probe_ops;
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500367 else
Steven Rostedtb6887d72009-02-17 12:32:04 -0500368 ops = &traceoff_probe_ops;
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500369
370 if (!param)
371 goto out_reg;
372
373 number = strsep(&param, ":");
374
375 if (!strlen(number))
376 goto out_reg;
377
378 /*
379 * We use the callback data field (which is a pointer)
380 * as our counter.
381 */
382 ret = strict_strtoul(number, 0, (unsigned long *)&count);
383 if (ret)
384 return ret;
385
386 out_reg:
Steven Rostedtb6887d72009-02-17 12:32:04 -0500387 ret = register_ftrace_function_probe(glob, ops, count);
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500388
Xiao Guangrong04aef322009-07-15 12:29:06 +0800389 return ret < 0 ? ret : 0;
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500390}
391
392static struct ftrace_func_command ftrace_traceon_cmd = {
393 .name = "traceon",
394 .func = ftrace_trace_onoff_callback,
395};
396
397static struct ftrace_func_command ftrace_traceoff_cmd = {
398 .name = "traceoff",
399 .func = ftrace_trace_onoff_callback,
400};
401
402static int __init init_func_cmd_traceon(void)
403{
404 int ret;
405
406 ret = register_ftrace_command(&ftrace_traceoff_cmd);
407 if (ret)
408 return ret;
409
410 ret = register_ftrace_command(&ftrace_traceon_cmd);
411 if (ret)
412 unregister_ftrace_command(&ftrace_traceoff_cmd);
413 return ret;
414}
415#else
416static inline int init_func_cmd_traceon(void)
417{
418 return 0;
419}
420#endif /* CONFIG_DYNAMIC_FTRACE */
421
Steven Rostedt1b29b012008-05-12 21:20:42 +0200422static __init int init_function_trace(void)
423{
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500424 init_func_cmd_traceon();
Steven Rostedt1b29b012008-05-12 21:20:42 +0200425 return register_tracer(&function_trace);
426}
Steven Rostedt1b29b012008-05-12 21:20:42 +0200427device_initcall(init_function_trace);
Steven Rostedt23b4ff32009-02-14 19:04:24 -0500428