Root/kernel/trace/trace_functions.c

1/*
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 */
12#include <linux/ring_buffer.h>
13#include <linux/debugfs.h>
14#include <linux/uaccess.h>
15#include <linux/ftrace.h>
16#include <linux/fs.h>
17
18#include "trace.h"
19
20/* function tracing enabled */
21static int ftrace_function_enabled;
22
23static struct trace_array *func_trace;
24
25static void tracing_start_function_trace(void);
26static void tracing_stop_function_trace(void);
27
28static int function_trace_init(struct trace_array *tr)
29{
30    func_trace = tr;
31    tr->cpu = get_cpu();
32    put_cpu();
33
34    tracing_start_cmdline_record();
35    tracing_start_function_trace();
36    return 0;
37}
38
39static void function_trace_reset(struct trace_array *tr)
40{
41    tracing_stop_function_trace();
42    tracing_stop_cmdline_record();
43}
44
45static void function_trace_start(struct trace_array *tr)
46{
47    tracing_reset_online_cpus(tr);
48}
49
50static void
51function_trace_call_preempt_only(unsigned long ip, unsigned long parent_ip)
52{
53    struct trace_array *tr = func_trace;
54    struct trace_array_cpu *data;
55    unsigned long flags;
56    long disabled;
57    int cpu;
58    int pc;
59
60    if (unlikely(!ftrace_function_enabled))
61        return;
62
63    pc = preempt_count();
64    preempt_disable_notrace();
65    local_save_flags(flags);
66    cpu = raw_smp_processor_id();
67    data = tr->data[cpu];
68    disabled = atomic_inc_return(&data->disabled);
69
70    if (likely(disabled == 1))
71        trace_function(tr, ip, parent_ip, flags, pc);
72
73    atomic_dec(&data->disabled);
74    preempt_enable_notrace();
75}
76
77static void
78function_trace_call(unsigned long ip, unsigned long parent_ip)
79{
80    struct trace_array *tr = func_trace;
81    struct trace_array_cpu *data;
82    unsigned long flags;
83    long disabled;
84    int cpu;
85    int pc;
86
87    if (unlikely(!ftrace_function_enabled))
88        return;
89
90    /*
91     * Need to use raw, since this must be called before the
92     * recursive protection is performed.
93     */
94    local_irq_save(flags);
95    cpu = raw_smp_processor_id();
96    data = tr->data[cpu];
97    disabled = atomic_inc_return(&data->disabled);
98
99    if (likely(disabled == 1)) {
100        pc = preempt_count();
101        trace_function(tr, ip, parent_ip, flags, pc);
102    }
103
104    atomic_dec(&data->disabled);
105    local_irq_restore(flags);
106}
107
108static void
109function_stack_trace_call(unsigned long ip, unsigned long parent_ip)
110{
111    struct trace_array *tr = func_trace;
112    struct trace_array_cpu *data;
113    unsigned long flags;
114    long disabled;
115    int cpu;
116    int pc;
117
118    if (unlikely(!ftrace_function_enabled))
119        return;
120
121    /*
122     * Need to use raw, since this must be called before the
123     * recursive protection is performed.
124     */
125    local_irq_save(flags);
126    cpu = raw_smp_processor_id();
127    data = tr->data[cpu];
128    disabled = atomic_inc_return(&data->disabled);
129
130    if (likely(disabled == 1)) {
131        pc = preempt_count();
132        trace_function(tr, ip, parent_ip, flags, pc);
133        /*
134         * skip over 5 funcs:
135         * __ftrace_trace_stack,
136         * __trace_stack,
137         * function_stack_trace_call
138         * ftrace_list_func
139         * ftrace_call
140         */
141        __trace_stack(tr, flags, 5, pc);
142    }
143
144    atomic_dec(&data->disabled);
145    local_irq_restore(flags);
146}
147
148
149static struct ftrace_ops trace_ops __read_mostly =
150{
151    .func = function_trace_call,
152};
153
154static struct ftrace_ops trace_stack_ops __read_mostly =
155{
156    .func = function_stack_trace_call,
157};
158
159/* Our two options */
160enum {
161    TRACE_FUNC_OPT_STACK = 0x1,
162};
163
164static struct tracer_opt func_opts[] = {
165#ifdef CONFIG_STACKTRACE
166    { TRACER_OPT(func_stack_trace, TRACE_FUNC_OPT_STACK) },
167#endif
168    { } /* Always set a last empty entry */
169};
170
171static struct tracer_flags func_flags = {
172    .val = 0, /* By default: all flags disabled */
173    .opts = func_opts
174};
175
176static void tracing_start_function_trace(void)
177{
178    ftrace_function_enabled = 0;
179
180    if (trace_flags & TRACE_ITER_PREEMPTONLY)
181        trace_ops.func = function_trace_call_preempt_only;
182    else
183        trace_ops.func = function_trace_call;
184
185    if (func_flags.val & TRACE_FUNC_OPT_STACK)
186        register_ftrace_function(&trace_stack_ops);
187    else
188        register_ftrace_function(&trace_ops);
189
190    ftrace_function_enabled = 1;
191}
192
193static void tracing_stop_function_trace(void)
194{
195    ftrace_function_enabled = 0;
196
197    if (func_flags.val & TRACE_FUNC_OPT_STACK)
198        unregister_ftrace_function(&trace_stack_ops);
199    else
200        unregister_ftrace_function(&trace_ops);
201}
202
203static int func_set_flag(u32 old_flags, u32 bit, int set)
204{
205    if (bit == TRACE_FUNC_OPT_STACK) {
206        /* do nothing if already set */
207        if (!!set == !!(func_flags.val & TRACE_FUNC_OPT_STACK))
208            return 0;
209
210        if (set) {
211            unregister_ftrace_function(&trace_ops);
212            register_ftrace_function(&trace_stack_ops);
213        } else {
214            unregister_ftrace_function(&trace_stack_ops);
215            register_ftrace_function(&trace_ops);
216        }
217
218        return 0;
219    }
220
221    return -EINVAL;
222}
223
224static struct tracer function_trace __read_mostly =
225{
226    .name = "function",
227    .init = function_trace_init,
228    .reset = function_trace_reset,
229    .start = function_trace_start,
230    .wait_pipe = poll_wait_pipe,
231    .flags = &func_flags,
232    .set_flag = func_set_flag,
233#ifdef CONFIG_FTRACE_SELFTEST
234    .selftest = trace_selftest_startup_function,
235#endif
236};
237
238#ifdef CONFIG_DYNAMIC_FTRACE
239static void
240ftrace_traceon(unsigned long ip, unsigned long parent_ip, void **data)
241{
242    long *count = (long *)data;
243
244    if (tracing_is_on())
245        return;
246
247    if (!*count)
248        return;
249
250    if (*count != -1)
251        (*count)--;
252
253    tracing_on();
254}
255
256static void
257ftrace_traceoff(unsigned long ip, unsigned long parent_ip, void **data)
258{
259    long *count = (long *)data;
260
261    if (!tracing_is_on())
262        return;
263
264    if (!*count)
265        return;
266
267    if (*count != -1)
268        (*count)--;
269
270    tracing_off();
271}
272
273static int
274ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
275             struct ftrace_probe_ops *ops, void *data);
276
277static struct ftrace_probe_ops traceon_probe_ops = {
278    .func = ftrace_traceon,
279    .print = ftrace_trace_onoff_print,
280};
281
282static struct ftrace_probe_ops traceoff_probe_ops = {
283    .func = ftrace_traceoff,
284    .print = ftrace_trace_onoff_print,
285};
286
287static int
288ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
289             struct ftrace_probe_ops *ops, void *data)
290{
291    long count = (long)data;
292
293    seq_printf(m, "%ps:", (void *)ip);
294
295    if (ops == &traceon_probe_ops)
296        seq_printf(m, "traceon");
297    else
298        seq_printf(m, "traceoff");
299
300    if (count == -1)
301        seq_printf(m, ":unlimited\n");
302    else
303        seq_printf(m, ":count=%ld\n", count);
304
305    return 0;
306}
307
308static int
309ftrace_trace_onoff_unreg(char *glob, char *cmd, char *param)
310{
311    struct ftrace_probe_ops *ops;
312
313    /* we register both traceon and traceoff to this callback */
314    if (strcmp(cmd, "traceon") == 0)
315        ops = &traceon_probe_ops;
316    else
317        ops = &traceoff_probe_ops;
318
319    unregister_ftrace_function_probe_func(glob, ops);
320
321    return 0;
322}
323
324static int
325ftrace_trace_onoff_callback(char *glob, char *cmd, char *param, int enable)
326{
327    struct ftrace_probe_ops *ops;
328    void *count = (void *)-1;
329    char *number;
330    int ret;
331
332    /* hash funcs only work with set_ftrace_filter */
333    if (!enable)
334        return -EINVAL;
335
336    if (glob[0] == '!')
337        return ftrace_trace_onoff_unreg(glob+1, cmd, param);
338
339    /* we register both traceon and traceoff to this callback */
340    if (strcmp(cmd, "traceon") == 0)
341        ops = &traceon_probe_ops;
342    else
343        ops = &traceoff_probe_ops;
344
345    if (!param)
346        goto out_reg;
347
348    number = strsep(&param, ":");
349
350    if (!strlen(number))
351        goto out_reg;
352
353    /*
354     * We use the callback data field (which is a pointer)
355     * as our counter.
356     */
357    ret = strict_strtoul(number, 0, (unsigned long *)&count);
358    if (ret)
359        return ret;
360
361 out_reg:
362    ret = register_ftrace_function_probe(glob, ops, count);
363
364    return ret < 0 ? ret : 0;
365}
366
367static struct ftrace_func_command ftrace_traceon_cmd = {
368    .name = "traceon",
369    .func = ftrace_trace_onoff_callback,
370};
371
372static struct ftrace_func_command ftrace_traceoff_cmd = {
373    .name = "traceoff",
374    .func = ftrace_trace_onoff_callback,
375};
376
377static int __init init_func_cmd_traceon(void)
378{
379    int ret;
380
381    ret = register_ftrace_command(&ftrace_traceoff_cmd);
382    if (ret)
383        return ret;
384
385    ret = register_ftrace_command(&ftrace_traceon_cmd);
386    if (ret)
387        unregister_ftrace_command(&ftrace_traceoff_cmd);
388    return ret;
389}
390#else
391static inline int init_func_cmd_traceon(void)
392{
393    return 0;
394}
395#endif /* CONFIG_DYNAMIC_FTRACE */
396
397static __init int init_function_trace(void)
398{
399    init_func_cmd_traceon();
400    return register_tracer(&function_trace);
401}
402device_initcall(init_function_trace);
403
404

Archive Download this file



interactive