trace_functions.c 8.23 KB
Newer Older
Steven Rostedt's avatar
Steven Rostedt committed
1
2
3
4
5
6
7
8
9
10
11
/*
 * ring buffer based function tracer
 *
 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
 * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
 *
 * Based on code from the latency_tracer, that is:
 *
 *  Copyright (C) 2004-2006 Ingo Molnar
 *  Copyright (C) 2004 William Lee Irwin III
 */
12
#include <linux/ring_buffer.h>
Steven Rostedt's avatar
Steven Rostedt committed
13
14
15
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/ftrace.h>
Ingo Molnar's avatar
Ingo Molnar committed
16
#include <linux/fs.h>
Steven Rostedt's avatar
Steven Rostedt committed
17
18
19

#include "trace.h"

20
21
22
/* function tracing enabled */
static int			ftrace_function_enabled;

23
24
static struct trace_array	*func_trace;

25
26
27
static void tracing_start_function_trace(void);
static void tracing_stop_function_trace(void);

28
static int function_trace_init(struct trace_array *tr)
Steven Rostedt's avatar
Steven Rostedt committed
29
{
30
	func_trace = tr;
31
32
33
	tr->cpu = get_cpu();
	put_cpu();

34
	tracing_start_cmdline_record();
Steven Rostedt's avatar
Steven Rostedt committed
35
	tracing_start_function_trace();
36
	return 0;
Steven Rostedt's avatar
Steven Rostedt committed
37
38
}

Ingo Molnar's avatar
Ingo Molnar committed
39
static void function_trace_reset(struct trace_array *tr)
Steven Rostedt's avatar
Steven Rostedt committed
40
{
41
42
	tracing_stop_function_trace();
	tracing_stop_cmdline_record();
Steven Rostedt's avatar
Steven Rostedt committed
43
44
}

45
46
static void function_trace_start(struct trace_array *tr)
{
47
	tracing_reset_online_cpus(tr);
48
49
}

50
51
52
53
54
55
56
static void
function_trace_call_preempt_only(unsigned long ip, unsigned long parent_ip)
{
	struct trace_array *tr = func_trace;
	struct trace_array_cpu *data;
	unsigned long flags;
	long disabled;
57
	int cpu;
58
59
60
61
62
63
	int pc;

	if (unlikely(!ftrace_function_enabled))
		return;

	pc = preempt_count();
64
	preempt_disable_notrace();
65
66
67
68
69
70
	local_save_flags(flags);
	cpu = raw_smp_processor_id();
	data = tr->data[cpu];
	disabled = atomic_inc_return(&data->disabled);

	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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
}

static void
function_trace_call(unsigned long ip, unsigned long parent_ip)
{
	struct trace_array *tr = func_trace;
	struct trace_array_cpu *data;
	unsigned long flags;
	long disabled;
	int cpu;
	int pc;

	if (unlikely(!ftrace_function_enabled))
		return;

	/*
	 * Need to use raw, since this must be called before the
	 * recursive protection is performed.
	 */
	local_irq_save(flags);
	cpu = raw_smp_processor_id();
	data = tr->data[cpu];
	disabled = atomic_inc_return(&data->disabled);

	if (likely(disabled == 1)) {
		pc = preempt_count();
101
		trace_function(tr, ip, parent_ip, flags, pc);
102
103
104
105
106
107
	}

	atomic_dec(&data->disabled);
	local_irq_restore(flags);
}

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
static void
function_stack_trace_call(unsigned long ip, unsigned long parent_ip)
{
	struct trace_array *tr = func_trace;
	struct trace_array_cpu *data;
	unsigned long flags;
	long disabled;
	int cpu;
	int pc;

	if (unlikely(!ftrace_function_enabled))
		return;

	/*
	 * Need to use raw, since this must be called before the
	 * recursive protection is performed.
	 */
	local_irq_save(flags);
	cpu = raw_smp_processor_id();
	data = tr->data[cpu];
	disabled = atomic_inc_return(&data->disabled);

	if (likely(disabled == 1)) {
		pc = preempt_count();
132
		trace_function(tr, ip, parent_ip, flags, pc);
133
134
135
136
137
138
139
140
		/*
		 * skip over 5 funcs:
		 *    __ftrace_trace_stack,
		 *    __trace_stack,
		 *    function_stack_trace_call
		 *    ftrace_list_func
		 *    ftrace_call
		 */
141
		__trace_stack(tr, flags, 5, pc);
142
143
144
145
146
147
	}

	atomic_dec(&data->disabled);
	local_irq_restore(flags);
}

148
149
150
151

static struct ftrace_ops trace_ops __read_mostly =
{
	.func = function_trace_call,
152
	.flags = FTRACE_OPS_FL_GLOBAL,
153
154
};

155
156
157
static struct ftrace_ops trace_stack_ops __read_mostly =
{
	.func = function_stack_trace_call,
158
	.flags = FTRACE_OPS_FL_GLOBAL,
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
};

/* Our two options */
enum {
	TRACE_FUNC_OPT_STACK = 0x1,
};

static struct tracer_opt func_opts[] = {
#ifdef CONFIG_STACKTRACE
	{ TRACER_OPT(func_stack_trace, TRACE_FUNC_OPT_STACK) },
#endif
	{ } /* Always set a last empty entry */
};

static struct tracer_flags func_flags = {
	.val = 0, /* By default: all flags disabled */
	.opts = func_opts
};

178
static void tracing_start_function_trace(void)
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
{
	ftrace_function_enabled = 0;

	if (trace_flags & TRACE_ITER_PREEMPTONLY)
		trace_ops.func = function_trace_call_preempt_only;
	else
		trace_ops.func = function_trace_call;

	if (func_flags.val & TRACE_FUNC_OPT_STACK)
		register_ftrace_function(&trace_stack_ops);
	else
		register_ftrace_function(&trace_ops);

	ftrace_function_enabled = 1;
}

195
static void tracing_stop_function_trace(void)
196
197
{
	ftrace_function_enabled = 0;
198
199
200
201
202

	if (func_flags.val & TRACE_FUNC_OPT_STACK)
		unregister_ftrace_function(&trace_stack_ops);
	else
		unregister_ftrace_function(&trace_ops);
203
204
}

205
206
207
208
209
210
211
static int func_set_flag(u32 old_flags, u32 bit, int set)
{
	if (bit == TRACE_FUNC_OPT_STACK) {
		/* do nothing if already set */
		if (!!set == !!(func_flags.val & TRACE_FUNC_OPT_STACK))
			return 0;

212
213
		if (set) {
			unregister_ftrace_function(&trace_ops);
214
			register_ftrace_function(&trace_stack_ops);
215
		} else {
216
			unregister_ftrace_function(&trace_stack_ops);
217
218
			register_ftrace_function(&trace_ops);
		}
219
220
221
222
223
224
225

		return 0;
	}

	return -EINVAL;
}

Steven Rostedt's avatar
Steven Rostedt committed
226
227
static struct tracer function_trace __read_mostly =
{
228
229
230
231
	.name		= "function",
	.init		= function_trace_init,
	.reset		= function_trace_reset,
	.start		= function_trace_start,
232
	.wait_pipe	= poll_wait_pipe,
233
234
	.flags		= &func_flags,
	.set_flag	= func_set_flag,
Steven Rostedt's avatar
Steven Rostedt committed
235
#ifdef CONFIG_FTRACE_SELFTEST
236
	.selftest	= trace_selftest_startup_function,
Steven Rostedt's avatar
Steven Rostedt committed
237
#endif
Steven Rostedt's avatar
Steven Rostedt committed
238
239
};

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#ifdef CONFIG_DYNAMIC_FTRACE
static void
ftrace_traceon(unsigned long ip, unsigned long parent_ip, void **data)
{
	long *count = (long *)data;

	if (tracing_is_on())
		return;

	if (!*count)
		return;

	if (*count != -1)
		(*count)--;

	tracing_on();
}

static void
ftrace_traceoff(unsigned long ip, unsigned long parent_ip, void **data)
{
	long *count = (long *)data;

	if (!tracing_is_on())
		return;

	if (!*count)
		return;

	if (*count != -1)
		(*count)--;

	tracing_off();
}

275
276
static int
ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
277
			 struct ftrace_probe_ops *ops, void *data);
278

279
static struct ftrace_probe_ops traceon_probe_ops = {
280
	.func			= ftrace_traceon,
281
	.print			= ftrace_trace_onoff_print,
282
283
};

284
static struct ftrace_probe_ops traceoff_probe_ops = {
285
	.func			= ftrace_traceoff,
286
	.print			= ftrace_trace_onoff_print,
287
288
};

289
290
static int
ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
291
			 struct ftrace_probe_ops *ops, void *data)
292
293
294
{
	long count = (long)data;

295
	seq_printf(m, "%ps:", (void *)ip);
296

297
	if (ops == &traceon_probe_ops)
298
299
300
301
		seq_printf(m, "traceon");
	else
		seq_printf(m, "traceoff");

302
303
304
	if (count == -1)
		seq_printf(m, ":unlimited\n");
	else
Li Zefan's avatar
Li Zefan committed
305
		seq_printf(m, ":count=%ld\n", count);
306
307
308
309

	return 0;
}

310
311
312
static int
ftrace_trace_onoff_unreg(char *glob, char *cmd, char *param)
{
313
	struct ftrace_probe_ops *ops;
314
315
316

	/* we register both traceon and traceoff to this callback */
	if (strcmp(cmd, "traceon") == 0)
317
		ops = &traceon_probe_ops;
318
	else
319
		ops = &traceoff_probe_ops;
320

321
	unregister_ftrace_function_probe_func(glob, ops);
322
323
324
325
326

	return 0;
}

static int
327
328
ftrace_trace_onoff_callback(struct ftrace_hash *hash,
			    char *glob, char *cmd, char *param, int enable)
329
{
330
	struct ftrace_probe_ops *ops;
331
332
333
334
335
336
337
338
339
340
341
342
343
	void *count = (void *)-1;
	char *number;
	int ret;

	/* hash funcs only work with set_ftrace_filter */
	if (!enable)
		return -EINVAL;

	if (glob[0] == '!')
		return ftrace_trace_onoff_unreg(glob+1, cmd, param);

	/* we register both traceon and traceoff to this callback */
	if (strcmp(cmd, "traceon") == 0)
344
		ops = &traceon_probe_ops;
345
	else
346
		ops = &traceoff_probe_ops;
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

	if (!param)
		goto out_reg;

	number = strsep(&param, ":");

	if (!strlen(number))
		goto out_reg;

	/*
	 * We use the callback data field (which is a pointer)
	 * as our counter.
	 */
	ret = strict_strtoul(number, 0, (unsigned long *)&count);
	if (ret)
		return ret;

 out_reg:
365
	ret = register_ftrace_function_probe(glob, ops, count);
366

367
	return ret < 0 ? ret : 0;
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
}

static struct ftrace_func_command ftrace_traceon_cmd = {
	.name			= "traceon",
	.func			= ftrace_trace_onoff_callback,
};

static struct ftrace_func_command ftrace_traceoff_cmd = {
	.name			= "traceoff",
	.func			= ftrace_trace_onoff_callback,
};

static int __init init_func_cmd_traceon(void)
{
	int ret;

	ret = register_ftrace_command(&ftrace_traceoff_cmd);
	if (ret)
		return ret;

	ret = register_ftrace_command(&ftrace_traceon_cmd);
	if (ret)
		unregister_ftrace_command(&ftrace_traceoff_cmd);
	return ret;
}
#else
static inline int init_func_cmd_traceon(void)
{
	return 0;
}
#endif /* CONFIG_DYNAMIC_FTRACE */

Steven Rostedt's avatar
Steven Rostedt committed
400
401
static __init int init_function_trace(void)
{
402
	init_func_cmd_traceon();
Steven Rostedt's avatar
Steven Rostedt committed
403
404
405
	return register_tracer(&function_trace);
}
device_initcall(init_function_trace);
406