trace_sysprof.c 6.5 KB
Newer Older
I
Ingo Molnar 已提交
1 2 3
/*
 * trace stack traces
 *
I
Ingo Molnar 已提交
4
 * Copyright (C) 2004-2008, Soeren Sandmann
I
Ingo Molnar 已提交
5 6 7 8
 * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
 * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
 */
#include <linux/kallsyms.h>
I
Ingo Molnar 已提交
9 10
#include <linux/debugfs.h>
#include <linux/hrtimer.h>
I
Ingo Molnar 已提交
11 12
#include <linux/uaccess.h>
#include <linux/ftrace.h>
I
Ingo Molnar 已提交
13
#include <linux/module.h>
14
#include <linux/irq.h>
I
Ingo Molnar 已提交
15
#include <linux/fs.h>
I
Ingo Molnar 已提交
16

S
Soeren Sandmann Pedersen 已提交
17 18
#include <asm/stacktrace.h>

I
Ingo Molnar 已提交
19 20
#include "trace.h"

21
static struct trace_array	*sysprof_trace;
I
Ingo Molnar 已提交
22 23
static int __read_mostly	tracer_enabled;

24
/*
I
Ingo Molnar 已提交
25
 * 1 msec sample interval by default:
26
 */
I
Ingo Molnar 已提交
27
static unsigned long sample_period = 1000000;
I
Ingo Molnar 已提交
28
static const unsigned int sample_max_depth = 512;
I
Ingo Molnar 已提交
29

I
Ingo Molnar 已提交
30
static DEFINE_MUTEX(sample_timer_lock);
I
Ingo Molnar 已提交
31 32 33 34 35
/*
 * Per CPU hrtimers that do the profiling:
 */
static DEFINE_PER_CPU(struct hrtimer, stack_trace_hrtimer);

36 37 38 39 40 41 42
struct stack_frame {
	const void __user	*next_fp;
	unsigned long		return_address;
};

static int copy_stack_frame(const void __user *fp, struct stack_frame *frame)
{
I
Ingo Molnar 已提交
43 44
	int ret;

45 46 47
	if (!access_ok(VERIFY_READ, fp, sizeof(*frame)))
		return 0;

I
Ingo Molnar 已提交
48 49 50 51 52
	ret = 1;
	pagefault_disable();
	if (__copy_from_user_inatomic(frame, fp, sizeof(*frame)))
		ret = 0;
	pagefault_enable();
53

I
Ingo Molnar 已提交
54
	return ret;
55 56
}

S
Soeren Sandmann Pedersen 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 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
struct backtrace_info {
	struct trace_array_cpu	*data;
	struct trace_array	*tr;
	int			pos;
};

static void
backtrace_warning_symbol(void *data, char *msg, unsigned long symbol)
{
	/* Ignore warnings */
}

static void backtrace_warning(void *data, char *msg)
{
	/* Ignore warnings */
}

static int backtrace_stack(void *data, char *name)
{
	/* Don't bother with IRQ stacks for now */
	return -1;
}

static void backtrace_address(void *data, unsigned long addr, int reliable)
{
	struct backtrace_info *info = data;

	if (info->pos < sample_max_depth && reliable) {
		__trace_special(info->tr, info->data, 1, addr, 0);

		info->pos++;
	}
}

const static struct stacktrace_ops backtrace_ops = {
	.warning		= backtrace_warning,
	.warning_symbol		= backtrace_warning_symbol,
	.stack			= backtrace_stack,
	.address		= backtrace_address,
};

98
static int
S
Soeren Sandmann Pedersen 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
trace_kernel(struct pt_regs *regs, struct trace_array *tr,
	     struct trace_array_cpu *data)
{
	struct backtrace_info info;
	unsigned long bp;
	char *stack;

	info.tr = tr;
	info.data = data;
	info.pos = 1;

	__trace_special(info.tr, info.data, 1, regs->ip, 0);

	stack = ((char *)regs + sizeof(struct pt_regs));
#ifdef CONFIG_FRAME_POINTER
	bp = regs->bp;
#else
	bp = 0;
#endif

	dump_trace(NULL, regs, (void *)stack, bp, &backtrace_ops, &info);

121
	return info.pos;
S
Soeren Sandmann Pedersen 已提交
122 123
}

124 125 126 127 128
static void timer_notify(struct pt_regs *regs, int cpu)
{
	struct trace_array_cpu *data;
	struct stack_frame frame;
	struct trace_array *tr;
I
Ingo Molnar 已提交
129
	const void __user *fp;
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
	int is_user;
	int i;

	if (!regs)
		return;

	tr = sysprof_trace;
	data = tr->data[cpu];
	is_user = user_mode(regs);

	if (!current || current->pid == 0)
		return;

	if (is_user && current->state != TASK_RUNNING)
		return;

S
Soeren Sandmann Pedersen 已提交
146
	__trace_special(tr, data, 0, 0, current->pid);
147

S
Soeren Sandmann Pedersen 已提交
148
	if (!is_user)
149 150 151
		i = trace_kernel(regs, tr, data);
	else
		i = 0;
152

153 154 155 156 157
	/*
	 * Trace user stack if we are not a kernel thread
	 */
	if (current->mm && i < sample_max_depth) {
		regs = (struct pt_regs *)current->thread.sp0 - 1;
158

159
		fp = (void __user *)regs->bp;
S
Soeren Sandmann Pedersen 已提交
160

161
		__trace_special(tr, data, 2, regs->ip, 0);
162

163
		while (i < sample_max_depth) {
164
			frame.next_fp = NULL;
165 166 167 168 169
			frame.return_address = 0;
			if (!copy_stack_frame(fp, &frame))
				break;
			if ((unsigned long)fp < regs->sp)
				break;
170

171 172 173 174 175 176 177 178
			__trace_special(tr, data, 2, frame.return_address,
					(unsigned long)fp);
			fp = frame.next_fp;

			i++;
		}

	}
179

I
Ingo Molnar 已提交
180 181 182
	/*
	 * Special trace entry if we overflow the max depth:
	 */
I
Ingo Molnar 已提交
183
	if (i == sample_max_depth)
T
Thomas Gleixner 已提交
184
		__trace_special(tr, data, -1, -1, -1);
185 186

	__trace_special(tr, data, 3, current->pid, i);
187 188
}

I
Ingo Molnar 已提交
189 190 191
static enum hrtimer_restart stack_trace_timer_fn(struct hrtimer *hrtimer)
{
	/* trace here */
192
	timer_notify(get_irq_regs(), smp_processor_id());
I
Ingo Molnar 已提交
193 194 195 196 197 198

	hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));

	return HRTIMER_RESTART;
}

199
static void start_stack_timer(void *unused)
I
Ingo Molnar 已提交
200
{
201
	struct hrtimer *hrtimer = &__get_cpu_var(stack_trace_hrtimer);
I
Ingo Molnar 已提交
202 203 204 205 206 207 208 209 210

	hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	hrtimer->function = stack_trace_timer_fn;

	hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL);
}

static void start_stack_timers(void)
{
211
	on_each_cpu(start_stack_timer, NULL, 1);
I
Ingo Molnar 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
}

static void stop_stack_timer(int cpu)
{
	struct hrtimer *hrtimer = &per_cpu(stack_trace_hrtimer, cpu);

	hrtimer_cancel(hrtimer);
}

static void stop_stack_timers(void)
{
	int cpu;

	for_each_online_cpu(cpu)
		stop_stack_timer(cpu);
}

T
Thomas Gleixner 已提交
229
static void stop_stack_trace(struct trace_array *tr)
I
Ingo Molnar 已提交
230
{
I
Ingo Molnar 已提交
231
	mutex_lock(&sample_timer_lock);
I
Ingo Molnar 已提交
232
	stop_stack_timers();
I
Ingo Molnar 已提交
233
	tracer_enabled = 0;
I
Ingo Molnar 已提交
234
	mutex_unlock(&sample_timer_lock);
I
Ingo Molnar 已提交
235 236
}

237
static int stack_trace_init(struct trace_array *tr)
I
Ingo Molnar 已提交
238
{
239
	sysprof_trace = tr;
I
Ingo Molnar 已提交
240

241 242 243 244
	mutex_lock(&sample_timer_lock);
	start_stack_timers();
	tracer_enabled = 1;
	mutex_unlock(&sample_timer_lock);
245
	return 0;
I
Ingo Molnar 已提交
246 247
}

T
Thomas Gleixner 已提交
248
static void stack_trace_reset(struct trace_array *tr)
I
Ingo Molnar 已提交
249
{
S
Steven Rostedt 已提交
250
	stop_stack_trace(tr);
I
Ingo Molnar 已提交
251 252 253 254 255 256 257 258
}

static struct tracer stack_trace __read_mostly =
{
	.name		= "sysprof",
	.init		= stack_trace_init,
	.reset		= stack_trace_reset,
#ifdef CONFIG_FTRACE_SELFTEST
259
	.selftest    = trace_selftest_startup_sysprof,
I
Ingo Molnar 已提交
260 261 262 263 264 265 266 267
#endif
};

__init static int init_stack_trace(void)
{
	return register_tracer(&stack_trace);
}
device_initcall(init_stack_trace);
I
Ingo Molnar 已提交
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

#define MAX_LONG_DIGITS 22

static ssize_t
sysprof_sample_read(struct file *filp, char __user *ubuf,
		    size_t cnt, loff_t *ppos)
{
	char buf[MAX_LONG_DIGITS];
	int r;

	r = sprintf(buf, "%ld\n", nsecs_to_usecs(sample_period));

	return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}

static ssize_t
sysprof_sample_write(struct file *filp, const char __user *ubuf,
		     size_t cnt, loff_t *ppos)
{
	char buf[MAX_LONG_DIGITS];
	unsigned long val;

	if (cnt > MAX_LONG_DIGITS-1)
		cnt = MAX_LONG_DIGITS-1;

	if (copy_from_user(&buf, ubuf, cnt))
		return -EFAULT;

	buf[cnt] = 0;

	val = simple_strtoul(buf, NULL, 10);
	/*
	 * Enforce a minimum sample period of 100 usecs:
	 */
	if (val < 100)
		val = 100;

	mutex_lock(&sample_timer_lock);
	stop_stack_timers();
	sample_period = val * 1000;
	start_stack_timers();
	mutex_unlock(&sample_timer_lock);

	return cnt;
}

static struct file_operations sysprof_sample_fops = {
	.read		= sysprof_sample_read,
	.write		= sysprof_sample_write,
};

void init_tracer_sysprof_debugfs(struct dentry *d_tracer)
{
	struct dentry *entry;

	entry = debugfs_create_file("sysprof_sample_period", 0644,
			d_tracer, NULL, &sysprof_sample_fops);
	if (entry)
		return;
	pr_warning("Could not create debugfs 'dyn_ftrace_total_info' entry\n");
}