trace_functions.c 8.21 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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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;
	int cpu, resched;
	int pc;

	if (unlikely(!ftrace_function_enabled))
		return;

	pc = preempt_count();
	resched = ftrace_preempt_disable();
	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
74
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

	atomic_dec(&data->disabled);
	ftrace_preempt_enable(resched);
}

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
152
153

static struct ftrace_ops trace_ops __read_mostly =
{
	.func = function_trace_call,
};

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

/* 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
};

176
static void tracing_start_function_trace(void)
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
{
	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;
}

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

	if (func_flags.val & TRACE_FUNC_OPT_STACK)
		unregister_ftrace_function(&trace_stack_ops);
	else
		unregister_ftrace_function(&trace_ops);
201
202
}

203
204
205
206
207
208
209
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;

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

		return 0;
	}

	return -EINVAL;
}

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

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
#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();
}

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

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

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

287
288
static int
ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
289
			 struct ftrace_probe_ops *ops, void *data)
290
291
292
293
294
295
296
{
	char str[KSYM_SYMBOL_LEN];
	long count = (long)data;

	kallsyms_lookup(ip, NULL, NULL, NULL, str);
	seq_printf(m, "%s:", str);

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
327
328

	return 0;
}

static int
ftrace_trace_onoff_callback(char *glob, char *cmd, char *param, int enable)
{
329
	struct ftrace_probe_ops *ops;
330
331
332
333
334
335
336
337
338
339
340
341
342
	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)
343
		ops = &traceon_probe_ops;
344
	else
345
		ops = &traceoff_probe_ops;
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363

	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:
364
	ret = register_ftrace_function_probe(glob, ops, count);
365
366
367
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

	return ret;
}

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
399
400
static __init int init_function_trace(void)
{
401
	init_func_cmd_traceon();
Steven Rostedt's avatar
Steven Rostedt committed
402
403
404
	return register_tracer(&function_trace);
}
device_initcall(init_function_trace);
405