xref: /linux/arch/um/kernel/stacktrace.c (revision 970e51feaddbc33ed0e7d187af7f69d1a12c7b6a)
1*970e51feSDaniel Walter /*
2*970e51feSDaniel Walter  * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3*970e51feSDaniel Walter  * Copyright (C) 2013 Richard Weinberger <richard@nod.at>
4*970e51feSDaniel Walter  * Copyright (C) 2014 Google Inc., Author: Daniel Walter <dwalter@google.com>
5*970e51feSDaniel Walter  *
6*970e51feSDaniel Walter  * This program is free software; you can redistribute it and/or modify
7*970e51feSDaniel Walter  * it under the terms of the GNU General Public License version 2 as
8*970e51feSDaniel Walter  * published by the Free Software Foundation.
9*970e51feSDaniel Walter  */
10*970e51feSDaniel Walter 
11*970e51feSDaniel Walter #include <linux/kallsyms.h>
12*970e51feSDaniel Walter #include <linux/kernel.h>
13*970e51feSDaniel Walter #include <linux/sched.h>
14*970e51feSDaniel Walter #include <linux/stacktrace.h>
15*970e51feSDaniel Walter #include <linux/module.h>
16*970e51feSDaniel Walter #include <linux/uaccess.h>
17*970e51feSDaniel Walter #include <asm/stacktrace.h>
18*970e51feSDaniel Walter 
19*970e51feSDaniel Walter void dump_trace(struct task_struct *tsk,
20*970e51feSDaniel Walter 		const struct stacktrace_ops *ops,
21*970e51feSDaniel Walter 		void *data)
22*970e51feSDaniel Walter {
23*970e51feSDaniel Walter 	int reliable = 0;
24*970e51feSDaniel Walter 	unsigned long *sp, bp, addr;
25*970e51feSDaniel Walter 	struct pt_regs *segv_regs = tsk->thread.segv_regs;
26*970e51feSDaniel Walter 	struct stack_frame *frame;
27*970e51feSDaniel Walter 
28*970e51feSDaniel Walter 	bp = get_frame_pointer(tsk, segv_regs);
29*970e51feSDaniel Walter 	sp = get_stack_pointer(tsk, segv_regs);
30*970e51feSDaniel Walter 
31*970e51feSDaniel Walter 	frame = (struct stack_frame *)bp;
32*970e51feSDaniel Walter 	while (((long) sp & (THREAD_SIZE-1)) != 0) {
33*970e51feSDaniel Walter 		addr = *sp;
34*970e51feSDaniel Walter 		if (__kernel_text_address(addr)) {
35*970e51feSDaniel Walter 			reliable = 0;
36*970e51feSDaniel Walter 			if ((unsigned long) sp == bp + sizeof(long)) {
37*970e51feSDaniel Walter 				frame = frame ? frame->next_frame : NULL;
38*970e51feSDaniel Walter 				bp = (unsigned long)frame;
39*970e51feSDaniel Walter 				reliable = 1;
40*970e51feSDaniel Walter 			}
41*970e51feSDaniel Walter 			ops->address(data, addr, reliable);
42*970e51feSDaniel Walter 		}
43*970e51feSDaniel Walter 		sp++;
44*970e51feSDaniel Walter 	}
45*970e51feSDaniel Walter }
46*970e51feSDaniel Walter 
47*970e51feSDaniel Walter static void save_addr(void *data, unsigned long address, int reliable)
48*970e51feSDaniel Walter {
49*970e51feSDaniel Walter 	struct stack_trace *trace = data;
50*970e51feSDaniel Walter 
51*970e51feSDaniel Walter 	if (!reliable)
52*970e51feSDaniel Walter 		return;
53*970e51feSDaniel Walter 	if (trace->nr_entries >= trace->max_entries)
54*970e51feSDaniel Walter 		return;
55*970e51feSDaniel Walter 
56*970e51feSDaniel Walter 	trace->entries[trace->nr_entries++] = address;
57*970e51feSDaniel Walter }
58*970e51feSDaniel Walter 
59*970e51feSDaniel Walter static const struct stacktrace_ops dump_ops = {
60*970e51feSDaniel Walter 	.address = save_addr
61*970e51feSDaniel Walter };
62*970e51feSDaniel Walter 
63*970e51feSDaniel Walter static void __save_stack_trace(struct task_struct *tsk, struct stack_trace *trace)
64*970e51feSDaniel Walter {
65*970e51feSDaniel Walter 	dump_trace(tsk, &dump_ops, trace);
66*970e51feSDaniel Walter 	if (trace->nr_entries < trace->max_entries)
67*970e51feSDaniel Walter 		trace->entries[trace->nr_entries++] = ULONG_MAX;
68*970e51feSDaniel Walter }
69*970e51feSDaniel Walter 
70*970e51feSDaniel Walter void save_stack_trace(struct stack_trace *trace)
71*970e51feSDaniel Walter {
72*970e51feSDaniel Walter 	__save_stack_trace(current, trace);
73*970e51feSDaniel Walter }
74*970e51feSDaniel Walter EXPORT_SYMBOL_GPL(save_stack_trace);
75*970e51feSDaniel Walter 
76*970e51feSDaniel Walter void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
77*970e51feSDaniel Walter {
78*970e51feSDaniel Walter 	__save_stack_trace(tsk, trace);
79*970e51feSDaniel Walter }
80*970e51feSDaniel Walter EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
81