1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21b29b018SSteven Rostedt /*
31b29b018SSteven Rostedt * ring buffer based function tracer
41b29b018SSteven Rostedt *
51b29b018SSteven Rostedt * Copyright (C) 2007-2008 Steven Rostedt <[email protected]>
61b29b018SSteven Rostedt * Copyright (C) 2008 Ingo Molnar <[email protected]>
71b29b018SSteven Rostedt *
81b29b018SSteven Rostedt * Based on code from the latency_tracer, that is:
91b29b018SSteven Rostedt *
101b29b018SSteven Rostedt * Copyright (C) 2004-2006 Ingo Molnar
116d49e352SNadia Yvette Chambers * Copyright (C) 2004 Nadia Yvette Chambers
121b29b018SSteven Rostedt */
1323b4ff3aSSteven Rostedt #include <linux/ring_buffer.h>
141b29b018SSteven Rostedt #include <linux/debugfs.h>
151b29b018SSteven Rostedt #include <linux/uaccess.h>
161b29b018SSteven Rostedt #include <linux/ftrace.h>
17f20a5806SSteven Rostedt (Red Hat) #include <linux/slab.h>
182e0f5761SIngo Molnar #include <linux/fs.h>
191b29b018SSteven Rostedt
201b29b018SSteven Rostedt #include "trace.h"
211b29b018SSteven Rostedt
22f20a5806SSteven Rostedt (Red Hat) static void tracing_start_function_trace(struct trace_array *tr);
23f20a5806SSteven Rostedt (Red Hat) static void tracing_stop_function_trace(struct trace_array *tr);
24f20a5806SSteven Rostedt (Red Hat) static void
25f20a5806SSteven Rostedt (Red Hat) function_trace_call(unsigned long ip, unsigned long parent_ip,
26d19ad077SSteven Rostedt (VMware) struct ftrace_ops *op, struct ftrace_regs *fregs);
27f20a5806SSteven Rostedt (Red Hat) static void
2876fe0337SSven Schnelle function_args_trace_call(unsigned long ip, unsigned long parent_ip,
2976fe0337SSven Schnelle struct ftrace_ops *op, struct ftrace_regs *fregs);
3076fe0337SSven Schnelle static void
31f20a5806SSteven Rostedt (Red Hat) function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
32d19ad077SSteven Rostedt (VMware) struct ftrace_ops *op, struct ftrace_regs *fregs);
3322db095dSYordan Karadzhov (VMware) static void
3422db095dSYordan Karadzhov (VMware) function_no_repeats_trace_call(unsigned long ip, unsigned long parent_ip,
3522db095dSYordan Karadzhov (VMware) struct ftrace_ops *op, struct ftrace_regs *fregs);
3622db095dSYordan Karadzhov (VMware) static void
3722db095dSYordan Karadzhov (VMware) function_stack_no_repeats_trace_call(unsigned long ip, unsigned long parent_ip,
3822db095dSYordan Karadzhov (VMware) struct ftrace_ops *op,
3922db095dSYordan Karadzhov (VMware) struct ftrace_regs *fregs);
40f20a5806SSteven Rostedt (Red Hat) static struct tracer_flags func_flags;
41a225cdd2SSteven Rostedt
42f20a5806SSteven Rostedt (Red Hat) /* Our option */
43f20a5806SSteven Rostedt (Red Hat) enum {
4422db095dSYordan Karadzhov (VMware)
454994891eSYordan Karadzhov (VMware) TRACE_FUNC_NO_OPTS = 0x0, /* No flags set. */
46f20a5806SSteven Rostedt (Red Hat) TRACE_FUNC_OPT_STACK = 0x1,
4722db095dSYordan Karadzhov (VMware) TRACE_FUNC_OPT_NO_REPEATS = 0x2,
4876fe0337SSven Schnelle TRACE_FUNC_OPT_ARGS = 0x4,
4922db095dSYordan Karadzhov (VMware)
5022db095dSYordan Karadzhov (VMware) /* Update this to next highest bit. */
5176fe0337SSven Schnelle TRACE_FUNC_OPT_HIGHEST_BIT = 0x8
52f20a5806SSteven Rostedt (Red Hat) };
5353614991SSteven Rostedt
5422db095dSYordan Karadzhov (VMware) #define TRACE_FUNC_OPT_MASK (TRACE_FUNC_OPT_HIGHEST_BIT - 1)
554994891eSYordan Karadzhov (VMware)
ftrace_allocate_ftrace_ops(struct trace_array * tr)564114fbfdSMasami Hiramatsu int ftrace_allocate_ftrace_ops(struct trace_array *tr)
57f20a5806SSteven Rostedt (Red Hat) {
58f20a5806SSteven Rostedt (Red Hat) struct ftrace_ops *ops;
59f20a5806SSteven Rostedt (Red Hat)
604114fbfdSMasami Hiramatsu /* The top level array uses the "global_ops" */
614114fbfdSMasami Hiramatsu if (tr->flags & TRACE_ARRAY_FL_GLOBAL)
624114fbfdSMasami Hiramatsu return 0;
634114fbfdSMasami Hiramatsu
64f20a5806SSteven Rostedt (Red Hat) ops = kzalloc(sizeof(*ops), GFP_KERNEL);
65f20a5806SSteven Rostedt (Red Hat) if (!ops)
66f20a5806SSteven Rostedt (Red Hat) return -ENOMEM;
67f20a5806SSteven Rostedt (Red Hat)
6848a42f5dSWei Yang /* Currently only the non stack version is supported */
69f20a5806SSteven Rostedt (Red Hat) ops->func = function_trace_call;
70a25d036dSSteven Rostedt (VMware) ops->flags = FTRACE_OPS_FL_PID;
71f20a5806SSteven Rostedt (Red Hat)
72f20a5806SSteven Rostedt (Red Hat) tr->ops = ops;
73f20a5806SSteven Rostedt (Red Hat) ops->private = tr;
744114fbfdSMasami Hiramatsu
75f20a5806SSteven Rostedt (Red Hat) return 0;
76f20a5806SSteven Rostedt (Red Hat) }
77a225cdd2SSteven Rostedt
ftrace_free_ftrace_ops(struct trace_array * tr)784114fbfdSMasami Hiramatsu void ftrace_free_ftrace_ops(struct trace_array *tr)
794114fbfdSMasami Hiramatsu {
804114fbfdSMasami Hiramatsu kfree(tr->ops);
814114fbfdSMasami Hiramatsu tr->ops = NULL;
824114fbfdSMasami Hiramatsu }
83591dffdaSSteven Rostedt (Red Hat)
ftrace_create_function_files(struct trace_array * tr,struct dentry * parent)84591dffdaSSteven Rostedt (Red Hat) int ftrace_create_function_files(struct trace_array *tr,
85591dffdaSSteven Rostedt (Red Hat) struct dentry *parent)
86591dffdaSSteven Rostedt (Red Hat) {
8726dda563SSteven Rostedt (VMware) int ret;
885d6c97c5SSteven Rostedt (Red Hat) /*
895d6c97c5SSteven Rostedt (Red Hat) * The top level array uses the "global_ops", and the files are
905d6c97c5SSteven Rostedt (Red Hat) * created on boot up.
915d6c97c5SSteven Rostedt (Red Hat) */
925d6c97c5SSteven Rostedt (Red Hat) if (tr->flags & TRACE_ARRAY_FL_GLOBAL)
935d6c97c5SSteven Rostedt (Red Hat) return 0;
945d6c97c5SSteven Rostedt (Red Hat)
954114fbfdSMasami Hiramatsu if (!tr->ops)
964114fbfdSMasami Hiramatsu return -EINVAL;
97591dffdaSSteven Rostedt (Red Hat)
98c132be2cSSteven Rostedt (VMware) ret = allocate_fgraph_ops(tr, tr->ops);
9926dda563SSteven Rostedt (VMware) if (ret) {
10026dda563SSteven Rostedt (VMware) kfree(tr->ops);
10126dda563SSteven Rostedt (VMware) return ret;
10226dda563SSteven Rostedt (VMware) }
10326dda563SSteven Rostedt (VMware)
104591dffdaSSteven Rostedt (Red Hat) ftrace_create_filter_files(tr->ops, parent);
105591dffdaSSteven Rostedt (Red Hat)
106591dffdaSSteven Rostedt (Red Hat) return 0;
107591dffdaSSteven Rostedt (Red Hat) }
108591dffdaSSteven Rostedt (Red Hat)
ftrace_destroy_function_files(struct trace_array * tr)109591dffdaSSteven Rostedt (Red Hat) void ftrace_destroy_function_files(struct trace_array *tr)
110591dffdaSSteven Rostedt (Red Hat) {
111591dffdaSSteven Rostedt (Red Hat) ftrace_destroy_filter_files(tr->ops);
1124114fbfdSMasami Hiramatsu ftrace_free_ftrace_ops(tr);
11326dda563SSteven Rostedt (VMware) free_fgraph_ops(tr);
114591dffdaSSteven Rostedt (Red Hat) }
115591dffdaSSteven Rostedt (Red Hat)
select_trace_function(u32 flags_val)1164994891eSYordan Karadzhov (VMware) static ftrace_func_t select_trace_function(u32 flags_val)
1174994891eSYordan Karadzhov (VMware) {
1184994891eSYordan Karadzhov (VMware) switch (flags_val & TRACE_FUNC_OPT_MASK) {
1194994891eSYordan Karadzhov (VMware) case TRACE_FUNC_NO_OPTS:
1204994891eSYordan Karadzhov (VMware) return function_trace_call;
12176fe0337SSven Schnelle case TRACE_FUNC_OPT_ARGS:
12276fe0337SSven Schnelle return function_args_trace_call;
1234994891eSYordan Karadzhov (VMware) case TRACE_FUNC_OPT_STACK:
1244994891eSYordan Karadzhov (VMware) return function_stack_trace_call;
12522db095dSYordan Karadzhov (VMware) case TRACE_FUNC_OPT_NO_REPEATS:
12622db095dSYordan Karadzhov (VMware) return function_no_repeats_trace_call;
12722db095dSYordan Karadzhov (VMware) case TRACE_FUNC_OPT_STACK | TRACE_FUNC_OPT_NO_REPEATS:
12822db095dSYordan Karadzhov (VMware) return function_stack_no_repeats_trace_call;
1294994891eSYordan Karadzhov (VMware) default:
1304994891eSYordan Karadzhov (VMware) return NULL;
1314994891eSYordan Karadzhov (VMware) }
1324994891eSYordan Karadzhov (VMware) }
1334994891eSYordan Karadzhov (VMware)
handle_func_repeats(struct trace_array * tr,u32 flags_val)13422db095dSYordan Karadzhov (VMware) static bool handle_func_repeats(struct trace_array *tr, u32 flags_val)
13522db095dSYordan Karadzhov (VMware) {
13622db095dSYordan Karadzhov (VMware) if (!tr->last_func_repeats &&
13722db095dSYordan Karadzhov (VMware) (flags_val & TRACE_FUNC_OPT_NO_REPEATS)) {
13822db095dSYordan Karadzhov (VMware) tr->last_func_repeats = alloc_percpu(struct trace_func_repeats);
13922db095dSYordan Karadzhov (VMware) if (!tr->last_func_repeats)
14022db095dSYordan Karadzhov (VMware) return false;
14122db095dSYordan Karadzhov (VMware) }
14222db095dSYordan Karadzhov (VMware)
14322db095dSYordan Karadzhov (VMware) return true;
14422db095dSYordan Karadzhov (VMware) }
14522db095dSYordan Karadzhov (VMware)
function_trace_init(struct trace_array * tr)146b6f11df2SArnaldo Carvalho de Melo static int function_trace_init(struct trace_array *tr)
1471b29b018SSteven Rostedt {
1484104d326SSteven Rostedt (Red Hat) ftrace_func_t func;
149591dffdaSSteven Rostedt (Red Hat) /*
150591dffdaSSteven Rostedt (Red Hat) * Instance trace_arrays get their ops allocated
151591dffdaSSteven Rostedt (Red Hat) * at instance creation. Unless it failed
152591dffdaSSteven Rostedt (Red Hat) * the allocation.
153591dffdaSSteven Rostedt (Red Hat) */
1544104d326SSteven Rostedt (Red Hat) if (!tr->ops)
155591dffdaSSteven Rostedt (Red Hat) return -ENOMEM;
1564104d326SSteven Rostedt (Red Hat)
1574994891eSYordan Karadzhov (VMware) func = select_trace_function(func_flags.val);
1584994891eSYordan Karadzhov (VMware) if (!func)
1594994891eSYordan Karadzhov (VMware) return -EINVAL;
1604104d326SSteven Rostedt (Red Hat)
16122db095dSYordan Karadzhov (VMware) if (!handle_func_repeats(tr, func_flags.val))
16222db095dSYordan Karadzhov (VMware) return -ENOMEM;
16322db095dSYordan Karadzhov (VMware)
1644104d326SSteven Rostedt (Red Hat) ftrace_init_array_ops(tr, func);
165f20a5806SSteven Rostedt (Red Hat)
16618d14ebdSQiujun Huang tr->array_buffer.cpu = raw_smp_processor_id();
16726bc83f4SSteven Rostedt
16841bc8144SSteven Rostedt tracing_start_cmdline_record();
169f20a5806SSteven Rostedt (Red Hat) tracing_start_function_trace(tr);
1701c80025aSFrederic Weisbecker return 0;
1711b29b018SSteven Rostedt }
1721b29b018SSteven Rostedt
function_trace_reset(struct trace_array * tr)173e309b41dSIngo Molnar static void function_trace_reset(struct trace_array *tr)
1741b29b018SSteven Rostedt {
175f20a5806SSteven Rostedt (Red Hat) tracing_stop_function_trace(tr);
176b6f11df2SArnaldo Carvalho de Melo tracing_stop_cmdline_record();
1774104d326SSteven Rostedt (Red Hat) ftrace_reset_array_ops(tr);
1781b29b018SSteven Rostedt }
1791b29b018SSteven Rostedt
function_trace_start(struct trace_array * tr)1809036990dSSteven Rostedt static void function_trace_start(struct trace_array *tr)
1819036990dSSteven Rostedt {
1821c5eb448SSteven Rostedt (VMware) tracing_reset_online_cpus(&tr->array_buffer);
1839036990dSSteven Rostedt }
1849036990dSSteven Rostedt
185166438a4SSteven Rostedt /* fregs are guaranteed not to be NULL if HAVE_DYNAMIC_FTRACE_WITH_ARGS is set */
186166438a4SSteven Rostedt #if defined(CONFIG_FUNCTION_GRAPH_TRACER) && defined(CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS)
18760b1f578SJeff Xie static __always_inline unsigned long
function_get_true_parent_ip(unsigned long parent_ip,struct ftrace_regs * fregs)18860b1f578SJeff Xie function_get_true_parent_ip(unsigned long parent_ip, struct ftrace_regs *fregs)
18960b1f578SJeff Xie {
19060b1f578SJeff Xie unsigned long true_parent_ip;
19160b1f578SJeff Xie int idx = 0;
19260b1f578SJeff Xie
19360b1f578SJeff Xie true_parent_ip = parent_ip;
19460b1f578SJeff Xie if (unlikely(parent_ip == (unsigned long)&return_to_handler) && fregs)
19560b1f578SJeff Xie true_parent_ip = ftrace_graph_ret_addr(current, &idx, parent_ip,
19660b1f578SJeff Xie (unsigned long *)ftrace_regs_get_stack_pointer(fregs));
19760b1f578SJeff Xie return true_parent_ip;
19860b1f578SJeff Xie }
19960b1f578SJeff Xie #else
20060b1f578SJeff Xie static __always_inline unsigned long
function_get_true_parent_ip(unsigned long parent_ip,struct ftrace_regs * fregs)20160b1f578SJeff Xie function_get_true_parent_ip(unsigned long parent_ip, struct ftrace_regs *fregs)
20260b1f578SJeff Xie {
20360b1f578SJeff Xie return parent_ip;
20460b1f578SJeff Xie }
20560b1f578SJeff Xie #endif
20660b1f578SJeff Xie
207bb3c3c95SSteven Rostedt static void
function_trace_call(unsigned long ip,unsigned long parent_ip,struct ftrace_ops * op,struct ftrace_regs * fregs)2082f5f6ad9SSteven Rostedt function_trace_call(unsigned long ip, unsigned long parent_ip,
209d19ad077SSteven Rostedt (VMware) struct ftrace_ops *op, struct ftrace_regs *fregs)
210bb3c3c95SSteven Rostedt {
211f20a5806SSteven Rostedt (Red Hat) struct trace_array *tr = op->private;
212bb3c3c95SSteven Rostedt struct trace_array_cpu *data;
21336590c50SSebastian Andrzej Siewior unsigned int trace_ctx;
214d41032a8SSteven Rostedt int bit;
215bb3c3c95SSteven Rostedt
216f20a5806SSteven Rostedt (Red Hat) if (unlikely(!tr->function_enabled))
217bb3c3c95SSteven Rostedt return;
218bb3c3c95SSteven Rostedt
219773c1670SSteven Rostedt (VMware) bit = ftrace_test_recursion_trylock(ip, parent_ip);
2206e4eb9cbSSteven Rostedt (VMware) if (bit < 0)
2216e4eb9cbSSteven Rostedt (VMware) return;
2226e4eb9cbSSteven Rostedt (VMware)
22360b1f578SJeff Xie parent_ip = function_get_true_parent_ip(parent_ip, fregs);
22460b1f578SJeff Xie
22557b76bedSSebastian Andrzej Siewior trace_ctx = tracing_gen_ctx_dec();
226897f68a4SSteven Rostedt
227e32540b1SLi Chen data = this_cpu_ptr(tr->array_buffer.data);
22836590c50SSebastian Andrzej Siewior if (!atomic_read(&data->disabled))
22976fe0337SSven Schnelle trace_function(tr, ip, parent_ip, trace_ctx, NULL);
23076fe0337SSven Schnelle
23176fe0337SSven Schnelle ftrace_test_recursion_unlock(bit);
23276fe0337SSven Schnelle }
23376fe0337SSven Schnelle
23476fe0337SSven Schnelle static void
function_args_trace_call(unsigned long ip,unsigned long parent_ip,struct ftrace_ops * op,struct ftrace_regs * fregs)23576fe0337SSven Schnelle function_args_trace_call(unsigned long ip, unsigned long parent_ip,
23676fe0337SSven Schnelle struct ftrace_ops *op, struct ftrace_regs *fregs)
23776fe0337SSven Schnelle {
23876fe0337SSven Schnelle struct trace_array *tr = op->private;
23976fe0337SSven Schnelle struct trace_array_cpu *data;
24076fe0337SSven Schnelle unsigned int trace_ctx;
24176fe0337SSven Schnelle int bit;
24276fe0337SSven Schnelle int cpu;
24376fe0337SSven Schnelle
24476fe0337SSven Schnelle if (unlikely(!tr->function_enabled))
24576fe0337SSven Schnelle return;
24676fe0337SSven Schnelle
24776fe0337SSven Schnelle bit = ftrace_test_recursion_trylock(ip, parent_ip);
24876fe0337SSven Schnelle if (bit < 0)
24976fe0337SSven Schnelle return;
25076fe0337SSven Schnelle
25176fe0337SSven Schnelle trace_ctx = tracing_gen_ctx();
25276fe0337SSven Schnelle
25376fe0337SSven Schnelle cpu = smp_processor_id();
25476fe0337SSven Schnelle data = per_cpu_ptr(tr->array_buffer.data, cpu);
25576fe0337SSven Schnelle if (!atomic_read(&data->disabled))
25676fe0337SSven Schnelle trace_function(tr, ip, parent_ip, trace_ctx, fregs);
25736590c50SSebastian Andrzej Siewior
2586e4eb9cbSSteven Rostedt (VMware) ftrace_test_recursion_unlock(bit);
259bb3c3c95SSteven Rostedt }
260bb3c3c95SSteven Rostedt
2612ee5b92aSSteven Rostedt (VMware) #ifdef CONFIG_UNWINDER_ORC
2622ee5b92aSSteven Rostedt (VMware) /*
2632ee5b92aSSteven Rostedt (VMware) * Skip 2:
2642ee5b92aSSteven Rostedt (VMware) *
2652ee5b92aSSteven Rostedt (VMware) * function_stack_trace_call()
2662ee5b92aSSteven Rostedt (VMware) * ftrace_call()
2672ee5b92aSSteven Rostedt (VMware) */
2682ee5b92aSSteven Rostedt (VMware) #define STACK_SKIP 2
2692ee5b92aSSteven Rostedt (VMware) #else
2702ee5b92aSSteven Rostedt (VMware) /*
2712ee5b92aSSteven Rostedt (VMware) * Skip 3:
2722ee5b92aSSteven Rostedt (VMware) * __trace_stack()
2732ee5b92aSSteven Rostedt (VMware) * function_stack_trace_call()
2742ee5b92aSSteven Rostedt (VMware) * ftrace_call()
2752ee5b92aSSteven Rostedt (VMware) */
2762ee5b92aSSteven Rostedt (VMware) #define STACK_SKIP 3
2772ee5b92aSSteven Rostedt (VMware) #endif
2782ee5b92aSSteven Rostedt (VMware)
279bb3c3c95SSteven Rostedt static void
function_stack_trace_call(unsigned long ip,unsigned long parent_ip,struct ftrace_ops * op,struct ftrace_regs * fregs)2802f5f6ad9SSteven Rostedt function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
281d19ad077SSteven Rostedt (VMware) struct ftrace_ops *op, struct ftrace_regs *fregs)
28253614991SSteven Rostedt {
283f20a5806SSteven Rostedt (Red Hat) struct trace_array *tr = op->private;
28453614991SSteven Rostedt struct trace_array_cpu *data;
28553614991SSteven Rostedt unsigned long flags;
28653614991SSteven Rostedt long disabled;
28753614991SSteven Rostedt int cpu;
28836590c50SSebastian Andrzej Siewior unsigned int trace_ctx;
2896c1f7f0aSTatsuya S int skip = STACK_SKIP;
29053614991SSteven Rostedt
291f20a5806SSteven Rostedt (Red Hat) if (unlikely(!tr->function_enabled))
29253614991SSteven Rostedt return;
29353614991SSteven Rostedt
29453614991SSteven Rostedt /*
29553614991SSteven Rostedt * Need to use raw, since this must be called before the
29653614991SSteven Rostedt * recursive protection is performed.
29753614991SSteven Rostedt */
29853614991SSteven Rostedt local_irq_save(flags);
29960b1f578SJeff Xie parent_ip = function_get_true_parent_ip(parent_ip, fregs);
30053614991SSteven Rostedt cpu = raw_smp_processor_id();
3011c5eb448SSteven Rostedt (VMware) data = per_cpu_ptr(tr->array_buffer.data, cpu);
30253614991SSteven Rostedt disabled = atomic_inc_return(&data->disabled);
30353614991SSteven Rostedt
30453614991SSteven Rostedt if (likely(disabled == 1)) {
30536590c50SSebastian Andrzej Siewior trace_ctx = tracing_gen_ctx_flags(flags);
30676fe0337SSven Schnelle trace_function(tr, ip, parent_ip, trace_ctx, NULL);
3076c1f7f0aSTatsuya S #ifdef CONFIG_UNWINDER_FRAME_POINTER
3086c1f7f0aSTatsuya S if (ftrace_pids_enabled(op))
3096c1f7f0aSTatsuya S skip++;
3106c1f7f0aSTatsuya S #endif
3116c1f7f0aSTatsuya S __trace_stack(tr, trace_ctx, skip);
31253614991SSteven Rostedt }
31353614991SSteven Rostedt
31453614991SSteven Rostedt atomic_dec(&data->disabled);
31553614991SSteven Rostedt local_irq_restore(flags);
31653614991SSteven Rostedt }
31753614991SSteven Rostedt
is_repeat_check(struct trace_array * tr,struct trace_func_repeats * last_info,unsigned long ip,unsigned long parent_ip)31822db095dSYordan Karadzhov (VMware) static inline bool is_repeat_check(struct trace_array *tr,
31922db095dSYordan Karadzhov (VMware) struct trace_func_repeats *last_info,
32022db095dSYordan Karadzhov (VMware) unsigned long ip, unsigned long parent_ip)
32122db095dSYordan Karadzhov (VMware) {
32222db095dSYordan Karadzhov (VMware) if (last_info->ip == ip &&
32322db095dSYordan Karadzhov (VMware) last_info->parent_ip == parent_ip &&
32422db095dSYordan Karadzhov (VMware) last_info->count < U16_MAX) {
32522db095dSYordan Karadzhov (VMware) last_info->ts_last_call =
32622db095dSYordan Karadzhov (VMware) ring_buffer_time_stamp(tr->array_buffer.buffer);
32722db095dSYordan Karadzhov (VMware) last_info->count++;
32822db095dSYordan Karadzhov (VMware) return true;
32922db095dSYordan Karadzhov (VMware) }
33022db095dSYordan Karadzhov (VMware)
33122db095dSYordan Karadzhov (VMware) return false;
33222db095dSYordan Karadzhov (VMware) }
33322db095dSYordan Karadzhov (VMware)
process_repeats(struct trace_array * tr,unsigned long ip,unsigned long parent_ip,struct trace_func_repeats * last_info,unsigned int trace_ctx)33422db095dSYordan Karadzhov (VMware) static inline void process_repeats(struct trace_array *tr,
33522db095dSYordan Karadzhov (VMware) unsigned long ip, unsigned long parent_ip,
33622db095dSYordan Karadzhov (VMware) struct trace_func_repeats *last_info,
33722db095dSYordan Karadzhov (VMware) unsigned int trace_ctx)
33822db095dSYordan Karadzhov (VMware) {
33922db095dSYordan Karadzhov (VMware) if (last_info->count) {
34022db095dSYordan Karadzhov (VMware) trace_last_func_repeats(tr, last_info, trace_ctx);
34122db095dSYordan Karadzhov (VMware) last_info->count = 0;
34222db095dSYordan Karadzhov (VMware) }
34322db095dSYordan Karadzhov (VMware)
34422db095dSYordan Karadzhov (VMware) last_info->ip = ip;
34522db095dSYordan Karadzhov (VMware) last_info->parent_ip = parent_ip;
34622db095dSYordan Karadzhov (VMware) }
34722db095dSYordan Karadzhov (VMware)
34822db095dSYordan Karadzhov (VMware) static void
function_no_repeats_trace_call(unsigned long ip,unsigned long parent_ip,struct ftrace_ops * op,struct ftrace_regs * fregs)34922db095dSYordan Karadzhov (VMware) function_no_repeats_trace_call(unsigned long ip, unsigned long parent_ip,
35022db095dSYordan Karadzhov (VMware) struct ftrace_ops *op,
35122db095dSYordan Karadzhov (VMware) struct ftrace_regs *fregs)
35222db095dSYordan Karadzhov (VMware) {
35322db095dSYordan Karadzhov (VMware) struct trace_func_repeats *last_info;
35422db095dSYordan Karadzhov (VMware) struct trace_array *tr = op->private;
35522db095dSYordan Karadzhov (VMware) struct trace_array_cpu *data;
35622db095dSYordan Karadzhov (VMware) unsigned int trace_ctx;
35722db095dSYordan Karadzhov (VMware) int bit;
35822db095dSYordan Karadzhov (VMware)
35922db095dSYordan Karadzhov (VMware) if (unlikely(!tr->function_enabled))
36022db095dSYordan Karadzhov (VMware) return;
36122db095dSYordan Karadzhov (VMware)
36222db095dSYordan Karadzhov (VMware) bit = ftrace_test_recursion_trylock(ip, parent_ip);
36322db095dSYordan Karadzhov (VMware) if (bit < 0)
36422db095dSYordan Karadzhov (VMware) return;
36522db095dSYordan Karadzhov (VMware)
36660b1f578SJeff Xie parent_ip = function_get_true_parent_ip(parent_ip, fregs);
367e32540b1SLi Chen data = this_cpu_ptr(tr->array_buffer.data);
36822db095dSYordan Karadzhov (VMware) if (atomic_read(&data->disabled))
36922db095dSYordan Karadzhov (VMware) goto out;
37022db095dSYordan Karadzhov (VMware)
37122db095dSYordan Karadzhov (VMware) /*
37222db095dSYordan Karadzhov (VMware) * An interrupt may happen at any place here. But as far as I can see,
37322db095dSYordan Karadzhov (VMware) * the only damage that this can cause is to mess up the repetition
37422db095dSYordan Karadzhov (VMware) * counter without valuable data being lost.
37522db095dSYordan Karadzhov (VMware) * TODO: think about a solution that is better than just hoping to be
37622db095dSYordan Karadzhov (VMware) * lucky.
37722db095dSYordan Karadzhov (VMware) */
378e32540b1SLi Chen last_info = this_cpu_ptr(tr->last_func_repeats);
37922db095dSYordan Karadzhov (VMware) if (is_repeat_check(tr, last_info, ip, parent_ip))
38022db095dSYordan Karadzhov (VMware) goto out;
38122db095dSYordan Karadzhov (VMware)
38257b76bedSSebastian Andrzej Siewior trace_ctx = tracing_gen_ctx_dec();
38322db095dSYordan Karadzhov (VMware) process_repeats(tr, ip, parent_ip, last_info, trace_ctx);
38422db095dSYordan Karadzhov (VMware)
38576fe0337SSven Schnelle trace_function(tr, ip, parent_ip, trace_ctx, NULL);
38622db095dSYordan Karadzhov (VMware)
38722db095dSYordan Karadzhov (VMware) out:
38822db095dSYordan Karadzhov (VMware) ftrace_test_recursion_unlock(bit);
38922db095dSYordan Karadzhov (VMware) }
39022db095dSYordan Karadzhov (VMware)
39122db095dSYordan Karadzhov (VMware) static void
function_stack_no_repeats_trace_call(unsigned long ip,unsigned long parent_ip,struct ftrace_ops * op,struct ftrace_regs * fregs)39222db095dSYordan Karadzhov (VMware) function_stack_no_repeats_trace_call(unsigned long ip, unsigned long parent_ip,
39322db095dSYordan Karadzhov (VMware) struct ftrace_ops *op,
39422db095dSYordan Karadzhov (VMware) struct ftrace_regs *fregs)
39522db095dSYordan Karadzhov (VMware) {
39622db095dSYordan Karadzhov (VMware) struct trace_func_repeats *last_info;
39722db095dSYordan Karadzhov (VMware) struct trace_array *tr = op->private;
39822db095dSYordan Karadzhov (VMware) struct trace_array_cpu *data;
39922db095dSYordan Karadzhov (VMware) unsigned long flags;
40022db095dSYordan Karadzhov (VMware) long disabled;
40122db095dSYordan Karadzhov (VMware) int cpu;
40222db095dSYordan Karadzhov (VMware) unsigned int trace_ctx;
40322db095dSYordan Karadzhov (VMware)
40422db095dSYordan Karadzhov (VMware) if (unlikely(!tr->function_enabled))
40522db095dSYordan Karadzhov (VMware) return;
40622db095dSYordan Karadzhov (VMware)
40722db095dSYordan Karadzhov (VMware) /*
40822db095dSYordan Karadzhov (VMware) * Need to use raw, since this must be called before the
40922db095dSYordan Karadzhov (VMware) * recursive protection is performed.
41022db095dSYordan Karadzhov (VMware) */
41122db095dSYordan Karadzhov (VMware) local_irq_save(flags);
41260b1f578SJeff Xie parent_ip = function_get_true_parent_ip(parent_ip, fregs);
41322db095dSYordan Karadzhov (VMware) cpu = raw_smp_processor_id();
41422db095dSYordan Karadzhov (VMware) data = per_cpu_ptr(tr->array_buffer.data, cpu);
41522db095dSYordan Karadzhov (VMware) disabled = atomic_inc_return(&data->disabled);
41622db095dSYordan Karadzhov (VMware)
41722db095dSYordan Karadzhov (VMware) if (likely(disabled == 1)) {
41822db095dSYordan Karadzhov (VMware) last_info = per_cpu_ptr(tr->last_func_repeats, cpu);
41922db095dSYordan Karadzhov (VMware) if (is_repeat_check(tr, last_info, ip, parent_ip))
42022db095dSYordan Karadzhov (VMware) goto out;
42122db095dSYordan Karadzhov (VMware)
42222db095dSYordan Karadzhov (VMware) trace_ctx = tracing_gen_ctx_flags(flags);
42322db095dSYordan Karadzhov (VMware) process_repeats(tr, ip, parent_ip, last_info, trace_ctx);
42422db095dSYordan Karadzhov (VMware)
42576fe0337SSven Schnelle trace_function(tr, ip, parent_ip, trace_ctx, NULL);
42622db095dSYordan Karadzhov (VMware) __trace_stack(tr, trace_ctx, STACK_SKIP);
42722db095dSYordan Karadzhov (VMware) }
42822db095dSYordan Karadzhov (VMware)
42922db095dSYordan Karadzhov (VMware) out:
43022db095dSYordan Karadzhov (VMware) atomic_dec(&data->disabled);
43122db095dSYordan Karadzhov (VMware) local_irq_restore(flags);
43222db095dSYordan Karadzhov (VMware) }
43322db095dSYordan Karadzhov (VMware)
43453614991SSteven Rostedt static struct tracer_opt func_opts[] = {
43553614991SSteven Rostedt #ifdef CONFIG_STACKTRACE
43653614991SSteven Rostedt { TRACER_OPT(func_stack_trace, TRACE_FUNC_OPT_STACK) },
43753614991SSteven Rostedt #endif
43822db095dSYordan Karadzhov (VMware) { TRACER_OPT(func-no-repeats, TRACE_FUNC_OPT_NO_REPEATS) },
43976fe0337SSven Schnelle #ifdef CONFIG_FUNCTION_TRACE_ARGS
44076fe0337SSven Schnelle { TRACER_OPT(func-args, TRACE_FUNC_OPT_ARGS) },
44176fe0337SSven Schnelle #endif
44253614991SSteven Rostedt { } /* Always set a last empty entry */
44353614991SSteven Rostedt };
44453614991SSteven Rostedt
44553614991SSteven Rostedt static struct tracer_flags func_flags = {
4464994891eSYordan Karadzhov (VMware) .val = TRACE_FUNC_NO_OPTS, /* By default: all flags disabled */
44753614991SSteven Rostedt .opts = func_opts
44853614991SSteven Rostedt };
44953614991SSteven Rostedt
tracing_start_function_trace(struct trace_array * tr)450f20a5806SSteven Rostedt (Red Hat) static void tracing_start_function_trace(struct trace_array *tr)
4513eb36aa0SSteven Rostedt {
452f20a5806SSteven Rostedt (Red Hat) tr->function_enabled = 0;
453f20a5806SSteven Rostedt (Red Hat) register_ftrace_function(tr->ops);
454f20a5806SSteven Rostedt (Red Hat) tr->function_enabled = 1;
4553eb36aa0SSteven Rostedt }
4563eb36aa0SSteven Rostedt
tracing_stop_function_trace(struct trace_array * tr)457f20a5806SSteven Rostedt (Red Hat) static void tracing_stop_function_trace(struct trace_array *tr)
4583eb36aa0SSteven Rostedt {
459f20a5806SSteven Rostedt (Red Hat) tr->function_enabled = 0;
460f20a5806SSteven Rostedt (Red Hat) unregister_ftrace_function(tr->ops);
4613eb36aa0SSteven Rostedt }
4623eb36aa0SSteven Rostedt
463d39cdd20SChunyu Hu static struct tracer function_trace;
464d39cdd20SChunyu Hu
4658c1a49aeSSteven Rostedt (Red Hat) static int
func_set_flag(struct trace_array * tr,u32 old_flags,u32 bit,int set)4668c1a49aeSSteven Rostedt (Red Hat) func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
46753614991SSteven Rostedt {
4684994891eSYordan Karadzhov (VMware) ftrace_func_t func;
4694994891eSYordan Karadzhov (VMware) u32 new_flags;
47053614991SSteven Rostedt
4714994891eSYordan Karadzhov (VMware) /* Do nothing if already set. */
4724994891eSYordan Karadzhov (VMware) if (!!set == !!(func_flags.val & bit))
4734994891eSYordan Karadzhov (VMware) return 0;
4744994891eSYordan Karadzhov (VMware)
4754994891eSYordan Karadzhov (VMware) /* We can change this flag only when not running. */
476d39cdd20SChunyu Hu if (tr->current_trace != &function_trace)
4774994891eSYordan Karadzhov (VMware) return 0;
4784994891eSYordan Karadzhov (VMware)
4794994891eSYordan Karadzhov (VMware) new_flags = (func_flags.val & ~bit) | (set ? bit : 0);
4804994891eSYordan Karadzhov (VMware) func = select_trace_function(new_flags);
4814994891eSYordan Karadzhov (VMware) if (!func)
4824994891eSYordan Karadzhov (VMware) return -EINVAL;
4834994891eSYordan Karadzhov (VMware)
4844994891eSYordan Karadzhov (VMware) /* Check if there's anything to change. */
4854994891eSYordan Karadzhov (VMware) if (tr->ops->func == func)
4864994891eSYordan Karadzhov (VMware) return 0;
487d39cdd20SChunyu Hu
48822db095dSYordan Karadzhov (VMware) if (!handle_func_repeats(tr, new_flags))
48922db095dSYordan Karadzhov (VMware) return -ENOMEM;
49022db095dSYordan Karadzhov (VMware)
491f20a5806SSteven Rostedt (Red Hat) unregister_ftrace_function(tr->ops);
4924994891eSYordan Karadzhov (VMware) tr->ops->func = func;
493f20a5806SSteven Rostedt (Red Hat) register_ftrace_function(tr->ops);
49453614991SSteven Rostedt
495f555f123SAnton Vorontsov return 0;
49653614991SSteven Rostedt }
49753614991SSteven Rostedt
4988f768993SSteven Rostedt (Red Hat) static struct tracer function_trace __tracer_data =
4991b29b018SSteven Rostedt {
5003ce83aeaSSteven Rostedt .name = "function",
5011b29b018SSteven Rostedt .init = function_trace_init,
5021b29b018SSteven Rostedt .reset = function_trace_reset,
5039036990dSSteven Rostedt .start = function_trace_start,
50453614991SSteven Rostedt .flags = &func_flags,
50553614991SSteven Rostedt .set_flag = func_set_flag,
506f20a5806SSteven Rostedt (Red Hat) .allow_instances = true,
50760a11774SSteven Rostedt #ifdef CONFIG_FTRACE_SELFTEST
50860a11774SSteven Rostedt .selftest = trace_selftest_startup_function,
50960a11774SSteven Rostedt #endif
5101b29b018SSteven Rostedt };
5111b29b018SSteven Rostedt
51223b4ff3aSSteven Rostedt #ifdef CONFIG_DYNAMIC_FTRACE
update_traceon_count(struct ftrace_probe_ops * ops,unsigned long ip,struct trace_array * tr,bool on,void * data)513fe014e24SSteven Rostedt (VMware) static void update_traceon_count(struct ftrace_probe_ops *ops,
5142290f2c5SSteven Rostedt (VMware) unsigned long ip,
5152290f2c5SSteven Rostedt (VMware) struct trace_array *tr, bool on,
5166e444319SSteven Rostedt (VMware) void *data)
51723b4ff3aSSteven Rostedt {
5186e444319SSteven Rostedt (VMware) struct ftrace_func_mapper *mapper = data;
519fe014e24SSteven Rostedt (VMware) long *count;
520fe014e24SSteven Rostedt (VMware) long old_count;
52123b4ff3aSSteven Rostedt
522a9ce7c36SSteven Rostedt (Red Hat) /*
523a9ce7c36SSteven Rostedt (Red Hat) * Tracing gets disabled (or enabled) once per count.
5240af26492SSteven Rostedt (Red Hat) * This function can be called at the same time on multiple CPUs.
525a9ce7c36SSteven Rostedt (Red Hat) * It is fine if both disable (or enable) tracing, as disabling
526a9ce7c36SSteven Rostedt (Red Hat) * (or enabling) the second time doesn't do anything as the
527a9ce7c36SSteven Rostedt (Red Hat) * state of the tracer is already disabled (or enabled).
528a9ce7c36SSteven Rostedt (Red Hat) * What needs to be synchronized in this case is that the count
529a9ce7c36SSteven Rostedt (Red Hat) * only gets decremented once, even if the tracer is disabled
530a9ce7c36SSteven Rostedt (Red Hat) * (or enabled) twice, as the second one is really a nop.
531a9ce7c36SSteven Rostedt (Red Hat) *
532a9ce7c36SSteven Rostedt (Red Hat) * The memory barriers guarantee that we only decrement the
533a9ce7c36SSteven Rostedt (Red Hat) * counter once. First the count is read to a local variable
534a9ce7c36SSteven Rostedt (Red Hat) * and a read barrier is used to make sure that it is loaded
535a9ce7c36SSteven Rostedt (Red Hat) * before checking if the tracer is in the state we want.
536a9ce7c36SSteven Rostedt (Red Hat) * If the tracer is not in the state we want, then the count
537a9ce7c36SSteven Rostedt (Red Hat) * is guaranteed to be the old count.
538a9ce7c36SSteven Rostedt (Red Hat) *
539a9ce7c36SSteven Rostedt (Red Hat) * Next the tracer is set to the state we want (disabled or enabled)
540a9ce7c36SSteven Rostedt (Red Hat) * then a write memory barrier is used to make sure that
541a9ce7c36SSteven Rostedt (Red Hat) * the new state is visible before changing the counter by
542a9ce7c36SSteven Rostedt (Red Hat) * one minus the old counter. This guarantees that another CPU
543a9ce7c36SSteven Rostedt (Red Hat) * executing this code will see the new state before seeing
5440af26492SSteven Rostedt (Red Hat) * the new counter value, and would not do anything if the new
545a9ce7c36SSteven Rostedt (Red Hat) * counter is seen.
546a9ce7c36SSteven Rostedt (Red Hat) *
547a9ce7c36SSteven Rostedt (Red Hat) * Note, there is no synchronization between this and a user
548a9ce7c36SSteven Rostedt (Red Hat) * setting the tracing_on file. But we currently don't care
549a9ce7c36SSteven Rostedt (Red Hat) * about that.
550a9ce7c36SSteven Rostedt (Red Hat) */
551fe014e24SSteven Rostedt (VMware) count = (long *)ftrace_func_mapper_find_ip(mapper, ip);
552fe014e24SSteven Rostedt (VMware) old_count = *count;
553fe014e24SSteven Rostedt (VMware)
554fe014e24SSteven Rostedt (VMware) if (old_count <= 0)
555a9ce7c36SSteven Rostedt (Red Hat) return;
55623b4ff3aSSteven Rostedt
557a9ce7c36SSteven Rostedt (Red Hat) /* Make sure we see count before checking tracing state */
558a9ce7c36SSteven Rostedt (Red Hat) smp_rmb();
55923b4ff3aSSteven Rostedt
5602290f2c5SSteven Rostedt (VMware) if (on == !!tracer_tracing_is_on(tr))
561a9ce7c36SSteven Rostedt (Red Hat) return;
562a9ce7c36SSteven Rostedt (Red Hat)
563a9ce7c36SSteven Rostedt (Red Hat) if (on)
5642290f2c5SSteven Rostedt (VMware) tracer_tracing_on(tr);
565a9ce7c36SSteven Rostedt (Red Hat) else
5662290f2c5SSteven Rostedt (VMware) tracer_tracing_off(tr);
567a9ce7c36SSteven Rostedt (Red Hat)
568a9ce7c36SSteven Rostedt (Red Hat) /* Make sure tracing state is visible before updating count */
569a9ce7c36SSteven Rostedt (Red Hat) smp_wmb();
570a9ce7c36SSteven Rostedt (Red Hat)
571a9ce7c36SSteven Rostedt (Red Hat) *count = old_count - 1;
5721c317143SSteven Rostedt (Red Hat) }
5731c317143SSteven Rostedt (Red Hat)
5741c317143SSteven Rostedt (Red Hat) static void
ftrace_traceon_count(unsigned long ip,unsigned long parent_ip,struct trace_array * tr,struct ftrace_probe_ops * ops,void * data)575bca6c8d0SSteven Rostedt (VMware) ftrace_traceon_count(unsigned long ip, unsigned long parent_ip,
576b5f081b5SSteven Rostedt (VMware) struct trace_array *tr, struct ftrace_probe_ops *ops,
5776e444319SSteven Rostedt (VMware) void *data)
5781c317143SSteven Rostedt (Red Hat) {
5792290f2c5SSteven Rostedt (VMware) update_traceon_count(ops, ip, tr, 1, data);
58023b4ff3aSSteven Rostedt }
58123b4ff3aSSteven Rostedt
58223b4ff3aSSteven Rostedt static void
ftrace_traceoff_count(unsigned long ip,unsigned long parent_ip,struct trace_array * tr,struct ftrace_probe_ops * ops,void * data)583bca6c8d0SSteven Rostedt (VMware) ftrace_traceoff_count(unsigned long ip, unsigned long parent_ip,
584b5f081b5SSteven Rostedt (VMware) struct trace_array *tr, struct ftrace_probe_ops *ops,
5856e444319SSteven Rostedt (VMware) void *data)
58623b4ff3aSSteven Rostedt {
5872290f2c5SSteven Rostedt (VMware) update_traceon_count(ops, ip, tr, 0, data);
58823b4ff3aSSteven Rostedt }
58923b4ff3aSSteven Rostedt
5908380d248SSteven Rostedt (Red Hat) static void
ftrace_traceon(unsigned long ip,unsigned long parent_ip,struct trace_array * tr,struct ftrace_probe_ops * ops,void * data)591bca6c8d0SSteven Rostedt (VMware) ftrace_traceon(unsigned long ip, unsigned long parent_ip,
592b5f081b5SSteven Rostedt (VMware) struct trace_array *tr, struct ftrace_probe_ops *ops,
5936e444319SSteven Rostedt (VMware) void *data)
5948380d248SSteven Rostedt (Red Hat) {
5952290f2c5SSteven Rostedt (VMware) if (tracer_tracing_is_on(tr))
5968380d248SSteven Rostedt (Red Hat) return;
5978380d248SSteven Rostedt (Red Hat)
5982290f2c5SSteven Rostedt (VMware) tracer_tracing_on(tr);
5998380d248SSteven Rostedt (Red Hat) }
6008380d248SSteven Rostedt (Red Hat)
6018380d248SSteven Rostedt (Red Hat) static void
ftrace_traceoff(unsigned long ip,unsigned long parent_ip,struct trace_array * tr,struct ftrace_probe_ops * ops,void * data)602bca6c8d0SSteven Rostedt (VMware) ftrace_traceoff(unsigned long ip, unsigned long parent_ip,
603b5f081b5SSteven Rostedt (VMware) struct trace_array *tr, struct ftrace_probe_ops *ops,
6046e444319SSteven Rostedt (VMware) void *data)
6058380d248SSteven Rostedt (Red Hat) {
6062290f2c5SSteven Rostedt (VMware) if (!tracer_tracing_is_on(tr))
6078380d248SSteven Rostedt (Red Hat) return;
6088380d248SSteven Rostedt (Red Hat)
6092290f2c5SSteven Rostedt (VMware) tracer_tracing_off(tr);
6108380d248SSteven Rostedt (Red Hat) }
6118380d248SSteven Rostedt (Red Hat)
6122ee5b92aSSteven Rostedt (VMware) #ifdef CONFIG_UNWINDER_ORC
613dd42cd3eSSteven Rostedt (Red Hat) /*
6142ee5b92aSSteven Rostedt (VMware) * Skip 3:
6152ee5b92aSSteven Rostedt (VMware) *
616dd42cd3eSSteven Rostedt (Red Hat) * function_trace_probe_call()
6172ee5b92aSSteven Rostedt (VMware) * ftrace_ops_assist_func()
618dd42cd3eSSteven Rostedt (Red Hat) * ftrace_call()
619dd42cd3eSSteven Rostedt (Red Hat) */
6202ee5b92aSSteven Rostedt (VMware) #define FTRACE_STACK_SKIP 3
6212ee5b92aSSteven Rostedt (VMware) #else
6222ee5b92aSSteven Rostedt (VMware) /*
6232ee5b92aSSteven Rostedt (VMware) * Skip 5:
6242ee5b92aSSteven Rostedt (VMware) *
6252ee5b92aSSteven Rostedt (VMware) * __trace_stack()
6262ee5b92aSSteven Rostedt (VMware) * ftrace_stacktrace()
6272ee5b92aSSteven Rostedt (VMware) * function_trace_probe_call()
6282ee5b92aSSteven Rostedt (VMware) * ftrace_ops_assist_func()
6292ee5b92aSSteven Rostedt (VMware) * ftrace_call()
6302ee5b92aSSteven Rostedt (VMware) */
6312ee5b92aSSteven Rostedt (VMware) #define FTRACE_STACK_SKIP 5
6322ee5b92aSSteven Rostedt (VMware) #endif
633e110e3d1SSteven Rostedt
trace_stack(struct trace_array * tr)634dcc19d28SSteven Rostedt (VMware) static __always_inline void trace_stack(struct trace_array *tr)
635dcc19d28SSteven Rostedt (VMware) {
636*11aff324Spengdonglin __trace_stack(tr, tracing_gen_ctx_dec(), FTRACE_STACK_SKIP);
637dcc19d28SSteven Rostedt (VMware) }
638dcc19d28SSteven Rostedt (VMware)
639dd42cd3eSSteven Rostedt (Red Hat) static void
ftrace_stacktrace(unsigned long ip,unsigned long parent_ip,struct trace_array * tr,struct ftrace_probe_ops * ops,void * data)640bca6c8d0SSteven Rostedt (VMware) ftrace_stacktrace(unsigned long ip, unsigned long parent_ip,
641b5f081b5SSteven Rostedt (VMware) struct trace_array *tr, struct ftrace_probe_ops *ops,
6426e444319SSteven Rostedt (VMware) void *data)
643dd42cd3eSSteven Rostedt (Red Hat) {
644dcc19d28SSteven Rostedt (VMware) trace_stack(tr);
645dd42cd3eSSteven Rostedt (Red Hat) }
6468380d248SSteven Rostedt (Red Hat)
647dd42cd3eSSteven Rostedt (Red Hat) static void
ftrace_stacktrace_count(unsigned long ip,unsigned long parent_ip,struct trace_array * tr,struct ftrace_probe_ops * ops,void * data)648bca6c8d0SSteven Rostedt (VMware) ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip,
649b5f081b5SSteven Rostedt (VMware) struct trace_array *tr, struct ftrace_probe_ops *ops,
6506e444319SSteven Rostedt (VMware) void *data)
651dd42cd3eSSteven Rostedt (Red Hat) {
6526e444319SSteven Rostedt (VMware) struct ftrace_func_mapper *mapper = data;
653fe014e24SSteven Rostedt (VMware) long *count;
654a9ce7c36SSteven Rostedt (Red Hat) long old_count;
655a9ce7c36SSteven Rostedt (Red Hat) long new_count;
656a9ce7c36SSteven Rostedt (Red Hat)
657fe014e24SSteven Rostedt (VMware) if (!tracing_is_on())
658fe014e24SSteven Rostedt (VMware) return;
659fe014e24SSteven Rostedt (VMware)
660fe014e24SSteven Rostedt (VMware) /* unlimited? */
661fe014e24SSteven Rostedt (VMware) if (!mapper) {
662dcc19d28SSteven Rostedt (VMware) trace_stack(tr);
663fe014e24SSteven Rostedt (VMware) return;
664fe014e24SSteven Rostedt (VMware) }
665fe014e24SSteven Rostedt (VMware)
666fe014e24SSteven Rostedt (VMware) count = (long *)ftrace_func_mapper_find_ip(mapper, ip);
667fe014e24SSteven Rostedt (VMware)
668a9ce7c36SSteven Rostedt (Red Hat) /*
669a9ce7c36SSteven Rostedt (Red Hat) * Stack traces should only execute the number of times the
670a9ce7c36SSteven Rostedt (Red Hat) * user specified in the counter.
671a9ce7c36SSteven Rostedt (Red Hat) */
672a9ce7c36SSteven Rostedt (Red Hat) do {
673a9ce7c36SSteven Rostedt (Red Hat) old_count = *count;
674a9ce7c36SSteven Rostedt (Red Hat)
675a9ce7c36SSteven Rostedt (Red Hat) if (!old_count)
676a9ce7c36SSteven Rostedt (Red Hat) return;
677a9ce7c36SSteven Rostedt (Red Hat)
678a9ce7c36SSteven Rostedt (Red Hat) new_count = old_count - 1;
679a9ce7c36SSteven Rostedt (Red Hat) new_count = cmpxchg(count, old_count, new_count);
680a9ce7c36SSteven Rostedt (Red Hat) if (new_count == old_count)
681dcc19d28SSteven Rostedt (VMware) trace_stack(tr);
682a9ce7c36SSteven Rostedt (Red Hat)
683fe014e24SSteven Rostedt (VMware) if (!tracing_is_on())
684fe014e24SSteven Rostedt (VMware) return;
685fe014e24SSteven Rostedt (VMware)
686a9ce7c36SSteven Rostedt (Red Hat) } while (new_count != old_count);
687a9ce7c36SSteven Rostedt (Red Hat) }
688a9ce7c36SSteven Rostedt (Red Hat)
update_count(struct ftrace_probe_ops * ops,unsigned long ip,void * data)6896e444319SSteven Rostedt (VMware) static int update_count(struct ftrace_probe_ops *ops, unsigned long ip,
6906e444319SSteven Rostedt (VMware) void *data)
691a9ce7c36SSteven Rostedt (Red Hat) {
6926e444319SSteven Rostedt (VMware) struct ftrace_func_mapper *mapper = data;
693fe014e24SSteven Rostedt (VMware) long *count = NULL;
694a9ce7c36SSteven Rostedt (Red Hat)
695fe014e24SSteven Rostedt (VMware) if (mapper)
696fe014e24SSteven Rostedt (VMware) count = (long *)ftrace_func_mapper_find_ip(mapper, ip);
697fe014e24SSteven Rostedt (VMware)
698fe014e24SSteven Rostedt (VMware) if (count) {
699fe014e24SSteven Rostedt (VMware) if (*count <= 0)
700a9ce7c36SSteven Rostedt (Red Hat) return 0;
701a9ce7c36SSteven Rostedt (Red Hat) (*count)--;
702fe014e24SSteven Rostedt (VMware) }
703a9ce7c36SSteven Rostedt (Red Hat)
704a9ce7c36SSteven Rostedt (Red Hat) return 1;
705dd42cd3eSSteven Rostedt (Red Hat) }
70623b4ff3aSSteven Rostedt
707ad71d889SSteven Rostedt (Red Hat) static void
ftrace_dump_probe(unsigned long ip,unsigned long parent_ip,struct trace_array * tr,struct ftrace_probe_ops * ops,void * data)708bca6c8d0SSteven Rostedt (VMware) ftrace_dump_probe(unsigned long ip, unsigned long parent_ip,
709b5f081b5SSteven Rostedt (VMware) struct trace_array *tr, struct ftrace_probe_ops *ops,
7106e444319SSteven Rostedt (VMware) void *data)
711ad71d889SSteven Rostedt (Red Hat) {
7126e444319SSteven Rostedt (VMware) if (update_count(ops, ip, data))
713ad71d889SSteven Rostedt (Red Hat) ftrace_dump(DUMP_ALL);
714ad71d889SSteven Rostedt (Red Hat) }
715ad71d889SSteven Rostedt (Red Hat)
71690e3c03cSSteven Rostedt (Red Hat) /* Only dump the current CPU buffer. */
71790e3c03cSSteven Rostedt (Red Hat) static void
ftrace_cpudump_probe(unsigned long ip,unsigned long parent_ip,struct trace_array * tr,struct ftrace_probe_ops * ops,void * data)718bca6c8d0SSteven Rostedt (VMware) ftrace_cpudump_probe(unsigned long ip, unsigned long parent_ip,
719b5f081b5SSteven Rostedt (VMware) struct trace_array *tr, struct ftrace_probe_ops *ops,
7206e444319SSteven Rostedt (VMware) void *data)
72190e3c03cSSteven Rostedt (Red Hat) {
7226e444319SSteven Rostedt (VMware) if (update_count(ops, ip, data))
72390e3c03cSSteven Rostedt (Red Hat) ftrace_dump(DUMP_ORIG);
72490e3c03cSSteven Rostedt (Red Hat) }
72590e3c03cSSteven Rostedt (Red Hat)
72623b4ff3aSSteven Rostedt static int
ftrace_probe_print(const char * name,struct seq_file * m,unsigned long ip,struct ftrace_probe_ops * ops,void * data)727dd42cd3eSSteven Rostedt (Red Hat) ftrace_probe_print(const char *name, struct seq_file *m,
7286e444319SSteven Rostedt (VMware) unsigned long ip, struct ftrace_probe_ops *ops,
7296e444319SSteven Rostedt (VMware) void *data)
730e110e3d1SSteven Rostedt {
7316e444319SSteven Rostedt (VMware) struct ftrace_func_mapper *mapper = data;
732fe014e24SSteven Rostedt (VMware) long *count = NULL;
733e110e3d1SSteven Rostedt
734dd42cd3eSSteven Rostedt (Red Hat) seq_printf(m, "%ps:%s", (void *)ip, name);
735e110e3d1SSteven Rostedt
736fe014e24SSteven Rostedt (VMware) if (mapper)
737fe014e24SSteven Rostedt (VMware) count = (long *)ftrace_func_mapper_find_ip(mapper, ip);
738fe014e24SSteven Rostedt (VMware)
739fe014e24SSteven Rostedt (VMware) if (count)
740fe014e24SSteven Rostedt (VMware) seq_printf(m, ":count=%ld\n", *count);
74135ebf1caSSteven Rostedt else
742fe014e24SSteven Rostedt (VMware) seq_puts(m, ":unlimited\n");
743e110e3d1SSteven Rostedt
744e110e3d1SSteven Rostedt return 0;
745e110e3d1SSteven Rostedt }
746e110e3d1SSteven Rostedt
747e110e3d1SSteven Rostedt static int
ftrace_traceon_print(struct seq_file * m,unsigned long ip,struct ftrace_probe_ops * ops,void * data)748dd42cd3eSSteven Rostedt (Red Hat) ftrace_traceon_print(struct seq_file *m, unsigned long ip,
749b5f081b5SSteven Rostedt (VMware) struct ftrace_probe_ops *ops,
750b5f081b5SSteven Rostedt (VMware) void *data)
75123b4ff3aSSteven Rostedt {
7526e444319SSteven Rostedt (VMware) return ftrace_probe_print("traceon", m, ip, ops, data);
753dd42cd3eSSteven Rostedt (Red Hat) }
754dd42cd3eSSteven Rostedt (Red Hat)
755dd42cd3eSSteven Rostedt (Red Hat) static int
ftrace_traceoff_print(struct seq_file * m,unsigned long ip,struct ftrace_probe_ops * ops,void * data)756dd42cd3eSSteven Rostedt (Red Hat) ftrace_traceoff_print(struct seq_file *m, unsigned long ip,
757dd42cd3eSSteven Rostedt (Red Hat) struct ftrace_probe_ops *ops, void *data)
758dd42cd3eSSteven Rostedt (Red Hat) {
7596e444319SSteven Rostedt (VMware) return ftrace_probe_print("traceoff", m, ip, ops, data);
760dd42cd3eSSteven Rostedt (Red Hat) }
761dd42cd3eSSteven Rostedt (Red Hat)
762dd42cd3eSSteven Rostedt (Red Hat) static int
ftrace_stacktrace_print(struct seq_file * m,unsigned long ip,struct ftrace_probe_ops * ops,void * data)763dd42cd3eSSteven Rostedt (Red Hat) ftrace_stacktrace_print(struct seq_file *m, unsigned long ip,
764dd42cd3eSSteven Rostedt (Red Hat) struct ftrace_probe_ops *ops, void *data)
765dd42cd3eSSteven Rostedt (Red Hat) {
7666e444319SSteven Rostedt (VMware) return ftrace_probe_print("stacktrace", m, ip, ops, data);
767dd42cd3eSSteven Rostedt (Red Hat) }
768dd42cd3eSSteven Rostedt (Red Hat)
769ad71d889SSteven Rostedt (Red Hat) static int
ftrace_dump_print(struct seq_file * m,unsigned long ip,struct ftrace_probe_ops * ops,void * data)770ad71d889SSteven Rostedt (Red Hat) ftrace_dump_print(struct seq_file *m, unsigned long ip,
771ad71d889SSteven Rostedt (Red Hat) struct ftrace_probe_ops *ops, void *data)
772ad71d889SSteven Rostedt (Red Hat) {
7736e444319SSteven Rostedt (VMware) return ftrace_probe_print("dump", m, ip, ops, data);
774ad71d889SSteven Rostedt (Red Hat) }
775ad71d889SSteven Rostedt (Red Hat)
77690e3c03cSSteven Rostedt (Red Hat) static int
ftrace_cpudump_print(struct seq_file * m,unsigned long ip,struct ftrace_probe_ops * ops,void * data)77790e3c03cSSteven Rostedt (Red Hat) ftrace_cpudump_print(struct seq_file *m, unsigned long ip,
77890e3c03cSSteven Rostedt (Red Hat) struct ftrace_probe_ops *ops, void *data)
77990e3c03cSSteven Rostedt (Red Hat) {
7806e444319SSteven Rostedt (VMware) return ftrace_probe_print("cpudump", m, ip, ops, data);
781fe014e24SSteven Rostedt (VMware) }
782fe014e24SSteven Rostedt (VMware)
783fe014e24SSteven Rostedt (VMware)
784fe014e24SSteven Rostedt (VMware) static int
ftrace_count_init(struct ftrace_probe_ops * ops,struct trace_array * tr,unsigned long ip,void * init_data,void ** data)785b5f081b5SSteven Rostedt (VMware) ftrace_count_init(struct ftrace_probe_ops *ops, struct trace_array *tr,
7866e444319SSteven Rostedt (VMware) unsigned long ip, void *init_data, void **data)
787fe014e24SSteven Rostedt (VMware) {
7886e444319SSteven Rostedt (VMware) struct ftrace_func_mapper *mapper = *data;
789fe014e24SSteven Rostedt (VMware)
7906e444319SSteven Rostedt (VMware) if (!mapper) {
7916e444319SSteven Rostedt (VMware) mapper = allocate_ftrace_func_mapper();
7926e444319SSteven Rostedt (VMware) if (!mapper)
7936e444319SSteven Rostedt (VMware) return -ENOMEM;
7946e444319SSteven Rostedt (VMware) *data = mapper;
7956e444319SSteven Rostedt (VMware) }
7966e444319SSteven Rostedt (VMware)
7976e444319SSteven Rostedt (VMware) return ftrace_func_mapper_add_ip(mapper, ip, init_data);
798fe014e24SSteven Rostedt (VMware) }
799fe014e24SSteven Rostedt (VMware)
800fe014e24SSteven Rostedt (VMware) static void
ftrace_count_free(struct ftrace_probe_ops * ops,struct trace_array * tr,unsigned long ip,void * data)801b5f081b5SSteven Rostedt (VMware) ftrace_count_free(struct ftrace_probe_ops *ops, struct trace_array *tr,
8026e444319SSteven Rostedt (VMware) unsigned long ip, void *data)
803fe014e24SSteven Rostedt (VMware) {
8046e444319SSteven Rostedt (VMware) struct ftrace_func_mapper *mapper = data;
8056e444319SSteven Rostedt (VMware)
8066e444319SSteven Rostedt (VMware) if (!ip) {
8076e444319SSteven Rostedt (VMware) free_ftrace_func_mapper(mapper, NULL);
8086e444319SSteven Rostedt (VMware) return;
8096e444319SSteven Rostedt (VMware) }
810fe014e24SSteven Rostedt (VMware)
811fe014e24SSteven Rostedt (VMware) ftrace_func_mapper_remove_ip(mapper, ip);
81290e3c03cSSteven Rostedt (Red Hat) }
81390e3c03cSSteven Rostedt (Red Hat)
814dd42cd3eSSteven Rostedt (Red Hat) static struct ftrace_probe_ops traceon_count_probe_ops = {
815dd42cd3eSSteven Rostedt (Red Hat) .func = ftrace_traceon_count,
816dd42cd3eSSteven Rostedt (Red Hat) .print = ftrace_traceon_print,
817fe014e24SSteven Rostedt (VMware) .init = ftrace_count_init,
818fe014e24SSteven Rostedt (VMware) .free = ftrace_count_free,
819dd42cd3eSSteven Rostedt (Red Hat) };
820dd42cd3eSSteven Rostedt (Red Hat)
821dd42cd3eSSteven Rostedt (Red Hat) static struct ftrace_probe_ops traceoff_count_probe_ops = {
822dd42cd3eSSteven Rostedt (Red Hat) .func = ftrace_traceoff_count,
823dd42cd3eSSteven Rostedt (Red Hat) .print = ftrace_traceoff_print,
824fe014e24SSteven Rostedt (VMware) .init = ftrace_count_init,
825fe014e24SSteven Rostedt (VMware) .free = ftrace_count_free,
826dd42cd3eSSteven Rostedt (Red Hat) };
827dd42cd3eSSteven Rostedt (Red Hat)
828dd42cd3eSSteven Rostedt (Red Hat) static struct ftrace_probe_ops stacktrace_count_probe_ops = {
829dd42cd3eSSteven Rostedt (Red Hat) .func = ftrace_stacktrace_count,
830dd42cd3eSSteven Rostedt (Red Hat) .print = ftrace_stacktrace_print,
831fe014e24SSteven Rostedt (VMware) .init = ftrace_count_init,
832fe014e24SSteven Rostedt (VMware) .free = ftrace_count_free,
833dd42cd3eSSteven Rostedt (Red Hat) };
834dd42cd3eSSteven Rostedt (Red Hat)
835ad71d889SSteven Rostedt (Red Hat) static struct ftrace_probe_ops dump_probe_ops = {
836ad71d889SSteven Rostedt (Red Hat) .func = ftrace_dump_probe,
837ad71d889SSteven Rostedt (Red Hat) .print = ftrace_dump_print,
838fe014e24SSteven Rostedt (VMware) .init = ftrace_count_init,
839fe014e24SSteven Rostedt (VMware) .free = ftrace_count_free,
840ad71d889SSteven Rostedt (Red Hat) };
841ad71d889SSteven Rostedt (Red Hat)
84290e3c03cSSteven Rostedt (Red Hat) static struct ftrace_probe_ops cpudump_probe_ops = {
84390e3c03cSSteven Rostedt (Red Hat) .func = ftrace_cpudump_probe,
84490e3c03cSSteven Rostedt (Red Hat) .print = ftrace_cpudump_print,
84590e3c03cSSteven Rostedt (Red Hat) };
84690e3c03cSSteven Rostedt (Red Hat)
847dd42cd3eSSteven Rostedt (Red Hat) static struct ftrace_probe_ops traceon_probe_ops = {
848dd42cd3eSSteven Rostedt (Red Hat) .func = ftrace_traceon,
849dd42cd3eSSteven Rostedt (Red Hat) .print = ftrace_traceon_print,
850dd42cd3eSSteven Rostedt (Red Hat) };
851dd42cd3eSSteven Rostedt (Red Hat)
852dd42cd3eSSteven Rostedt (Red Hat) static struct ftrace_probe_ops traceoff_probe_ops = {
853dd42cd3eSSteven Rostedt (Red Hat) .func = ftrace_traceoff,
854dd42cd3eSSteven Rostedt (Red Hat) .print = ftrace_traceoff_print,
855dd42cd3eSSteven Rostedt (Red Hat) };
856dd42cd3eSSteven Rostedt (Red Hat)
857dd42cd3eSSteven Rostedt (Red Hat) static struct ftrace_probe_ops stacktrace_probe_ops = {
858dd42cd3eSSteven Rostedt (Red Hat) .func = ftrace_stacktrace,
859dd42cd3eSSteven Rostedt (Red Hat) .print = ftrace_stacktrace_print,
860dd42cd3eSSteven Rostedt (Red Hat) };
861dd42cd3eSSteven Rostedt (Red Hat)
862dd42cd3eSSteven Rostedt (Red Hat) static int
ftrace_trace_probe_callback(struct trace_array * tr,struct ftrace_probe_ops * ops,struct ftrace_hash * hash,char * glob,char * cmd,char * param,int enable)86304ec7bb6SSteven Rostedt (VMware) ftrace_trace_probe_callback(struct trace_array *tr,
86404ec7bb6SSteven Rostedt (VMware) struct ftrace_probe_ops *ops,
865dd42cd3eSSteven Rostedt (Red Hat) struct ftrace_hash *hash, char *glob,
866dd42cd3eSSteven Rostedt (Red Hat) char *cmd, char *param, int enable)
867dd42cd3eSSteven Rostedt (Red Hat) {
86823b4ff3aSSteven Rostedt void *count = (void *)-1;
86923b4ff3aSSteven Rostedt char *number;
87023b4ff3aSSteven Rostedt int ret;
87123b4ff3aSSteven Rostedt
87223b4ff3aSSteven Rostedt /* hash funcs only work with set_ftrace_filter */
87323b4ff3aSSteven Rostedt if (!enable)
87423b4ff3aSSteven Rostedt return -EINVAL;
87523b4ff3aSSteven Rostedt
876d3d532d7SSteven Rostedt (VMware) if (glob[0] == '!')
8777b60f3d8SSteven Rostedt (VMware) return unregister_ftrace_function_probe_func(glob+1, tr, ops);
8788b8fa62cSSteven Rostedt (Red Hat)
87923b4ff3aSSteven Rostedt if (!param)
88023b4ff3aSSteven Rostedt goto out_reg;
88123b4ff3aSSteven Rostedt
88223b4ff3aSSteven Rostedt number = strsep(¶m, ":");
88323b4ff3aSSteven Rostedt
88423b4ff3aSSteven Rostedt if (!strlen(number))
88523b4ff3aSSteven Rostedt goto out_reg;
88623b4ff3aSSteven Rostedt
88723b4ff3aSSteven Rostedt /*
88823b4ff3aSSteven Rostedt * We use the callback data field (which is a pointer)
88923b4ff3aSSteven Rostedt * as our counter.
89023b4ff3aSSteven Rostedt */
891bcd83ea6SDaniel Walter ret = kstrtoul(number, 0, (unsigned long *)&count);
89223b4ff3aSSteven Rostedt if (ret)
89323b4ff3aSSteven Rostedt return ret;
89423b4ff3aSSteven Rostedt
89523b4ff3aSSteven Rostedt out_reg:
89604ec7bb6SSteven Rostedt (VMware) ret = register_ftrace_function_probe(glob, tr, ops, count);
89723b4ff3aSSteven Rostedt
89804aef32dSXiao Guangrong return ret < 0 ? ret : 0;
89923b4ff3aSSteven Rostedt }
90023b4ff3aSSteven Rostedt
901dd42cd3eSSteven Rostedt (Red Hat) static int
ftrace_trace_onoff_callback(struct trace_array * tr,struct ftrace_hash * hash,char * glob,char * cmd,char * param,int enable)90204ec7bb6SSteven Rostedt (VMware) ftrace_trace_onoff_callback(struct trace_array *tr, struct ftrace_hash *hash,
903dd42cd3eSSteven Rostedt (Red Hat) char *glob, char *cmd, char *param, int enable)
904dd42cd3eSSteven Rostedt (Red Hat) {
905dd42cd3eSSteven Rostedt (Red Hat) struct ftrace_probe_ops *ops;
906dd42cd3eSSteven Rostedt (Red Hat)
9070f179765SSteven Rostedt (VMware) if (!tr)
9080f179765SSteven Rostedt (VMware) return -ENODEV;
9090f179765SSteven Rostedt (VMware)
910dd42cd3eSSteven Rostedt (Red Hat) /* we register both traceon and traceoff to this callback */
911dd42cd3eSSteven Rostedt (Red Hat) if (strcmp(cmd, "traceon") == 0)
912dd42cd3eSSteven Rostedt (Red Hat) ops = param ? &traceon_count_probe_ops : &traceon_probe_ops;
913dd42cd3eSSteven Rostedt (Red Hat) else
914dd42cd3eSSteven Rostedt (Red Hat) ops = param ? &traceoff_count_probe_ops : &traceoff_probe_ops;
915dd42cd3eSSteven Rostedt (Red Hat)
91604ec7bb6SSteven Rostedt (VMware) return ftrace_trace_probe_callback(tr, ops, hash, glob, cmd,
917dd42cd3eSSteven Rostedt (Red Hat) param, enable);
918dd42cd3eSSteven Rostedt (Red Hat) }
919dd42cd3eSSteven Rostedt (Red Hat)
920dd42cd3eSSteven Rostedt (Red Hat) static int
ftrace_stacktrace_callback(struct trace_array * tr,struct ftrace_hash * hash,char * glob,char * cmd,char * param,int enable)92104ec7bb6SSteven Rostedt (VMware) ftrace_stacktrace_callback(struct trace_array *tr, struct ftrace_hash *hash,
922dd42cd3eSSteven Rostedt (Red Hat) char *glob, char *cmd, char *param, int enable)
923dd42cd3eSSteven Rostedt (Red Hat) {
924dd42cd3eSSteven Rostedt (Red Hat) struct ftrace_probe_ops *ops;
925dd42cd3eSSteven Rostedt (Red Hat)
9260f179765SSteven Rostedt (VMware) if (!tr)
9270f179765SSteven Rostedt (VMware) return -ENODEV;
9280f179765SSteven Rostedt (VMware)
929dd42cd3eSSteven Rostedt (Red Hat) ops = param ? &stacktrace_count_probe_ops : &stacktrace_probe_ops;
930dd42cd3eSSteven Rostedt (Red Hat)
93104ec7bb6SSteven Rostedt (VMware) return ftrace_trace_probe_callback(tr, ops, hash, glob, cmd,
932dd42cd3eSSteven Rostedt (Red Hat) param, enable);
933dd42cd3eSSteven Rostedt (Red Hat) }
934dd42cd3eSSteven Rostedt (Red Hat)
935ad71d889SSteven Rostedt (Red Hat) static int
ftrace_dump_callback(struct trace_array * tr,struct ftrace_hash * hash,char * glob,char * cmd,char * param,int enable)93604ec7bb6SSteven Rostedt (VMware) ftrace_dump_callback(struct trace_array *tr, struct ftrace_hash *hash,
937ad71d889SSteven Rostedt (Red Hat) char *glob, char *cmd, char *param, int enable)
938ad71d889SSteven Rostedt (Red Hat) {
939ad71d889SSteven Rostedt (Red Hat) struct ftrace_probe_ops *ops;
940ad71d889SSteven Rostedt (Red Hat)
9410f179765SSteven Rostedt (VMware) if (!tr)
9420f179765SSteven Rostedt (VMware) return -ENODEV;
9430f179765SSteven Rostedt (VMware)
944ad71d889SSteven Rostedt (Red Hat) ops = &dump_probe_ops;
945ad71d889SSteven Rostedt (Red Hat)
946ad71d889SSteven Rostedt (Red Hat) /* Only dump once. */
94704ec7bb6SSteven Rostedt (VMware) return ftrace_trace_probe_callback(tr, ops, hash, glob, cmd,
948ad71d889SSteven Rostedt (Red Hat) "1", enable);
949ad71d889SSteven Rostedt (Red Hat) }
950ad71d889SSteven Rostedt (Red Hat)
95190e3c03cSSteven Rostedt (Red Hat) static int
ftrace_cpudump_callback(struct trace_array * tr,struct ftrace_hash * hash,char * glob,char * cmd,char * param,int enable)95204ec7bb6SSteven Rostedt (VMware) ftrace_cpudump_callback(struct trace_array *tr, struct ftrace_hash *hash,
95390e3c03cSSteven Rostedt (Red Hat) char *glob, char *cmd, char *param, int enable)
95490e3c03cSSteven Rostedt (Red Hat) {
95590e3c03cSSteven Rostedt (Red Hat) struct ftrace_probe_ops *ops;
95690e3c03cSSteven Rostedt (Red Hat)
9570f179765SSteven Rostedt (VMware) if (!tr)
9580f179765SSteven Rostedt (VMware) return -ENODEV;
9590f179765SSteven Rostedt (VMware)
96090e3c03cSSteven Rostedt (Red Hat) ops = &cpudump_probe_ops;
96190e3c03cSSteven Rostedt (Red Hat)
96290e3c03cSSteven Rostedt (Red Hat) /* Only dump once. */
96304ec7bb6SSteven Rostedt (VMware) return ftrace_trace_probe_callback(tr, ops, hash, glob, cmd,
96490e3c03cSSteven Rostedt (Red Hat) "1", enable);
96590e3c03cSSteven Rostedt (Red Hat) }
96690e3c03cSSteven Rostedt (Red Hat)
96723b4ff3aSSteven Rostedt static struct ftrace_func_command ftrace_traceon_cmd = {
96823b4ff3aSSteven Rostedt .name = "traceon",
96923b4ff3aSSteven Rostedt .func = ftrace_trace_onoff_callback,
97023b4ff3aSSteven Rostedt };
97123b4ff3aSSteven Rostedt
97223b4ff3aSSteven Rostedt static struct ftrace_func_command ftrace_traceoff_cmd = {
97323b4ff3aSSteven Rostedt .name = "traceoff",
97423b4ff3aSSteven Rostedt .func = ftrace_trace_onoff_callback,
97523b4ff3aSSteven Rostedt };
97623b4ff3aSSteven Rostedt
977dd42cd3eSSteven Rostedt (Red Hat) static struct ftrace_func_command ftrace_stacktrace_cmd = {
978dd42cd3eSSteven Rostedt (Red Hat) .name = "stacktrace",
979dd42cd3eSSteven Rostedt (Red Hat) .func = ftrace_stacktrace_callback,
980dd42cd3eSSteven Rostedt (Red Hat) };
981dd42cd3eSSteven Rostedt (Red Hat)
982ad71d889SSteven Rostedt (Red Hat) static struct ftrace_func_command ftrace_dump_cmd = {
983ad71d889SSteven Rostedt (Red Hat) .name = "dump",
984ad71d889SSteven Rostedt (Red Hat) .func = ftrace_dump_callback,
985ad71d889SSteven Rostedt (Red Hat) };
986ad71d889SSteven Rostedt (Red Hat)
98790e3c03cSSteven Rostedt (Red Hat) static struct ftrace_func_command ftrace_cpudump_cmd = {
98890e3c03cSSteven Rostedt (Red Hat) .name = "cpudump",
98990e3c03cSSteven Rostedt (Red Hat) .func = ftrace_cpudump_callback,
99090e3c03cSSteven Rostedt (Red Hat) };
99190e3c03cSSteven Rostedt (Red Hat)
init_func_cmd_traceon(void)99223b4ff3aSSteven Rostedt static int __init init_func_cmd_traceon(void)
99323b4ff3aSSteven Rostedt {
99423b4ff3aSSteven Rostedt int ret;
99523b4ff3aSSteven Rostedt
99623b4ff3aSSteven Rostedt ret = register_ftrace_command(&ftrace_traceoff_cmd);
99723b4ff3aSSteven Rostedt if (ret)
99823b4ff3aSSteven Rostedt return ret;
99923b4ff3aSSteven Rostedt
100023b4ff3aSSteven Rostedt ret = register_ftrace_command(&ftrace_traceon_cmd);
100123b4ff3aSSteven Rostedt if (ret)
1002ad71d889SSteven Rostedt (Red Hat) goto out_free_traceoff;
1003dd42cd3eSSteven Rostedt (Red Hat)
1004dd42cd3eSSteven Rostedt (Red Hat) ret = register_ftrace_command(&ftrace_stacktrace_cmd);
1005ad71d889SSteven Rostedt (Red Hat) if (ret)
1006ad71d889SSteven Rostedt (Red Hat) goto out_free_traceon;
1007ad71d889SSteven Rostedt (Red Hat)
1008ad71d889SSteven Rostedt (Red Hat) ret = register_ftrace_command(&ftrace_dump_cmd);
1009ad71d889SSteven Rostedt (Red Hat) if (ret)
1010ad71d889SSteven Rostedt (Red Hat) goto out_free_stacktrace;
1011ad71d889SSteven Rostedt (Red Hat)
101290e3c03cSSteven Rostedt (Red Hat) ret = register_ftrace_command(&ftrace_cpudump_cmd);
101390e3c03cSSteven Rostedt (Red Hat) if (ret)
101490e3c03cSSteven Rostedt (Red Hat) goto out_free_dump;
101590e3c03cSSteven Rostedt (Red Hat)
1016ad71d889SSteven Rostedt (Red Hat) return 0;
1017ad71d889SSteven Rostedt (Red Hat)
101890e3c03cSSteven Rostedt (Red Hat) out_free_dump:
101990e3c03cSSteven Rostedt (Red Hat) unregister_ftrace_command(&ftrace_dump_cmd);
1020ad71d889SSteven Rostedt (Red Hat) out_free_stacktrace:
1021ad71d889SSteven Rostedt (Red Hat) unregister_ftrace_command(&ftrace_stacktrace_cmd);
1022ad71d889SSteven Rostedt (Red Hat) out_free_traceon:
1023dd42cd3eSSteven Rostedt (Red Hat) unregister_ftrace_command(&ftrace_traceon_cmd);
1024ad71d889SSteven Rostedt (Red Hat) out_free_traceoff:
1025ad71d889SSteven Rostedt (Red Hat) unregister_ftrace_command(&ftrace_traceoff_cmd);
1026ad71d889SSteven Rostedt (Red Hat)
102723b4ff3aSSteven Rostedt return ret;
102823b4ff3aSSteven Rostedt }
102923b4ff3aSSteven Rostedt #else
init_func_cmd_traceon(void)103023b4ff3aSSteven Rostedt static inline int init_func_cmd_traceon(void)
103123b4ff3aSSteven Rostedt {
103223b4ff3aSSteven Rostedt return 0;
103323b4ff3aSSteven Rostedt }
103423b4ff3aSSteven Rostedt #endif /* CONFIG_DYNAMIC_FTRACE */
103523b4ff3aSSteven Rostedt
init_function_trace(void)1036dbeafd0dSSteven Rostedt (VMware) __init int init_function_trace(void)
10371b29b018SSteven Rostedt {
103823b4ff3aSSteven Rostedt init_func_cmd_traceon();
10391b29b018SSteven Rostedt return register_tracer(&function_trace);
10401b29b018SSteven Rostedt }
1041