aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um
diff options
context:
space:
mode:
authorDaniel Walter <dwalter@google.com>2014-08-20 05:56:00 -0400
committerRichard Weinberger <richard@nod.at>2014-10-13 15:46:25 -0400
commit970e51feaddbc33ed0e7d187af7f69d1a12c7b6a (patch)
treedbe47fda5a1271dc5404c9c96d7965a870320fc2 /arch/um
parent2a2361228c5e6d8c1733f00653481de918598e50 (diff)
um: Add support for CONFIG_STACKTRACE
Add stacktrace support for User Mode Linux Signed-off-by: Daniel Walter <dwalter@google.com> Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'arch/um')
-rw-r--r--arch/um/Kconfig.common3
-rw-r--r--arch/um/include/asm/stacktrace.h42
-rw-r--r--arch/um/kernel/Makefile1
-rw-r--r--arch/um/kernel/stacktrace.c80
-rw-r--r--arch/um/kernel/sysrq.c69
5 files changed, 142 insertions, 53 deletions
diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common
index 6915d28cf118..87bc86821bc9 100644
--- a/arch/um/Kconfig.common
+++ b/arch/um/Kconfig.common
@@ -39,7 +39,8 @@ config LOCKDEP_SUPPORT
39 39
40config STACKTRACE_SUPPORT 40config STACKTRACE_SUPPORT
41 bool 41 bool
42 default n 42 default y
43 select STACKTRACE
43 44
44config GENERIC_CALIBRATE_DELAY 45config GENERIC_CALIBRATE_DELAY
45 bool 46 bool
diff --git a/arch/um/include/asm/stacktrace.h b/arch/um/include/asm/stacktrace.h
new file mode 100644
index 000000000000..9a864328c67f
--- /dev/null
+++ b/arch/um/include/asm/stacktrace.h
@@ -0,0 +1,42 @@
1#ifndef _ASM_UML_STACKTRACE_H
2#define _ASM_UML_STACKTRACE_H
3
4#include <linux/uaccess.h>
5#include <linux/ptrace.h>
6
7struct stack_frame {
8 struct stack_frame *next_frame;
9 unsigned long return_address;
10};
11
12struct stacktrace_ops {
13 void (*address)(void *data, unsigned long address, int reliable);
14};
15
16#ifdef CONFIG_FRAME_POINTER
17static inline unsigned long
18get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs)
19{
20 if (!task || task == current)
21 return segv_regs ? PT_REGS_BP(segv_regs) : current_bp();
22 return KSTK_EBP(task);
23}
24#else
25static inline unsigned long
26get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs)
27{
28 return 0;
29}
30#endif
31
32static inline unsigned long
33*get_stack_pointer(struct task_struct *task, struct pt_regs *segv_regs)
34{
35 if (!task || task == current)
36 return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp();
37 return (unsigned long *)KSTK_ESP(task);
38}
39
40void dump_trace(struct task_struct *tsk, const struct stacktrace_ops *ops, void *data);
41
42#endif /* _ASM_UML_STACKTRACE_H */
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile
index d8b78a03855c..2d840a070c8b 100644
--- a/arch/um/kernel/Makefile
+++ b/arch/um/kernel/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
19obj-$(CONFIG_GPROF) += gprof_syms.o 19obj-$(CONFIG_GPROF) += gprof_syms.o
20obj-$(CONFIG_GCOV) += gmon_syms.o 20obj-$(CONFIG_GCOV) += gmon_syms.o
21obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 21obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
22obj-$(CONFIG_STACKTRACE) += stacktrace.o
22 23
23USER_OBJS := config.o 24USER_OBJS := config.o
24 25
diff --git a/arch/um/kernel/stacktrace.c b/arch/um/kernel/stacktrace.c
new file mode 100644
index 000000000000..ebe7bcf62684
--- /dev/null
+++ b/arch/um/kernel/stacktrace.c
@@ -0,0 +1,80 @@
1/*
2 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3 * Copyright (C) 2013 Richard Weinberger <richard@nod.at>
4 * Copyright (C) 2014 Google Inc., Author: Daniel Walter <dwalter@google.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/kallsyms.h>
12#include <linux/kernel.h>
13#include <linux/sched.h>
14#include <linux/stacktrace.h>
15#include <linux/module.h>
16#include <linux/uaccess.h>
17#include <asm/stacktrace.h>
18
19void dump_trace(struct task_struct *tsk,
20 const struct stacktrace_ops *ops,
21 void *data)
22{
23 int reliable = 0;
24 unsigned long *sp, bp, addr;
25 struct pt_regs *segv_regs = tsk->thread.segv_regs;
26 struct stack_frame *frame;
27
28 bp = get_frame_pointer(tsk, segv_regs);
29 sp = get_stack_pointer(tsk, segv_regs);
30
31 frame = (struct stack_frame *)bp;
32 while (((long) sp & (THREAD_SIZE-1)) != 0) {
33 addr = *sp;
34 if (__kernel_text_address(addr)) {
35 reliable = 0;
36 if ((unsigned long) sp == bp + sizeof(long)) {
37 frame = frame ? frame->next_frame : NULL;
38 bp = (unsigned long)frame;
39 reliable = 1;
40 }
41 ops->address(data, addr, reliable);
42 }
43 sp++;
44 }
45}
46
47static void save_addr(void *data, unsigned long address, int reliable)
48{
49 struct stack_trace *trace = data;
50
51 if (!reliable)
52 return;
53 if (trace->nr_entries >= trace->max_entries)
54 return;
55
56 trace->entries[trace->nr_entries++] = address;
57}
58
59static const struct stacktrace_ops dump_ops = {
60 .address = save_addr
61};
62
63static void __save_stack_trace(struct task_struct *tsk, struct stack_trace *trace)
64{
65 dump_trace(tsk, &dump_ops, trace);
66 if (trace->nr_entries < trace->max_entries)
67 trace->entries[trace->nr_entries++] = ULONG_MAX;
68}
69
70void save_stack_trace(struct stack_trace *trace)
71{
72 __save_stack_trace(current, trace);
73}
74EXPORT_SYMBOL_GPL(save_stack_trace);
75
76void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
77{
78 __save_stack_trace(tsk, trace);
79}
80EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c
index 799d7e413bf5..894c8d303cda 100644
--- a/arch/um/kernel/sysrq.c
+++ b/arch/um/kernel/sysrq.c
@@ -12,57 +12,20 @@
12#include <linux/module.h> 12#include <linux/module.h>
13#include <linux/sched.h> 13#include <linux/sched.h>
14#include <asm/sysrq.h> 14#include <asm/sysrq.h>
15#include <asm/stacktrace.h>
15#include <os.h> 16#include <os.h>
16 17
17struct stack_frame { 18static void _print_addr(void *data, unsigned long address, int reliable)
18 struct stack_frame *next_frame;
19 unsigned long return_address;
20};
21
22static void do_stack_trace(unsigned long *sp, unsigned long bp)
23{ 19{
24 int reliable; 20 pr_info(" [<%08lx>]", address);
25 unsigned long addr; 21 pr_cont(" %s", reliable ? "" : "? ");
26 struct stack_frame *frame = (struct stack_frame *)bp; 22 print_symbol("%s", address);
27 23 pr_cont("\n");
28 printk(KERN_INFO "Call Trace:\n");
29 while (((long) sp & (THREAD_SIZE-1)) != 0) {
30 addr = *sp;
31 if (__kernel_text_address(addr)) {
32 reliable = 0;
33 if ((unsigned long) sp == bp + sizeof(long)) {
34 frame = frame ? frame->next_frame : NULL;
35 bp = (unsigned long)frame;
36 reliable = 1;
37 }
38
39 printk(KERN_INFO " [<%08lx>]", addr);
40 printk(KERN_CONT " %s", reliable ? "" : "? ");
41 print_symbol(KERN_CONT "%s", addr);
42 printk(KERN_CONT "\n");
43 }
44 sp++;
45 }
46 printk(KERN_INFO "\n");
47} 24}
48 25
49static unsigned long get_frame_pointer(struct task_struct *task, 26static const struct stacktrace_ops stackops = {
50 struct pt_regs *segv_regs) 27 .address = _print_addr
51{ 28};
52 if (!task || task == current)
53 return segv_regs ? PT_REGS_BP(segv_regs) : current_bp();
54 else
55 return KSTK_EBP(task);
56}
57
58static unsigned long *get_stack_pointer(struct task_struct *task,
59 struct pt_regs *segv_regs)
60{
61 if (!task || task == current)
62 return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp();
63 else
64 return (unsigned long *)KSTK_ESP(task);
65}
66 29
67void show_stack(struct task_struct *task, unsigned long *stack) 30void show_stack(struct task_struct *task, unsigned long *stack)
68{ 31{
@@ -71,7 +34,7 @@ void show_stack(struct task_struct *task, unsigned long *stack)
71 int i; 34 int i;
72 35
73 if (!segv_regs && os_is_signal_stack()) { 36 if (!segv_regs && os_is_signal_stack()) {
74 printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler," 37 pr_err("Received SIGSEGV in SIGSEGV handler,"
75 " aborting stack trace!\n"); 38 " aborting stack trace!\n");
76 return; 39 return;
77 } 40 }
@@ -83,16 +46,18 @@ void show_stack(struct task_struct *task, unsigned long *stack)
83 if (!stack) 46 if (!stack)
84 sp = get_stack_pointer(task, segv_regs); 47 sp = get_stack_pointer(task, segv_regs);
85 48
86 printk(KERN_INFO "Stack:\n"); 49 pr_info("Stack:\n");
87 stack = sp; 50 stack = sp;
88 for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) { 51 for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) {
89 if (kstack_end(stack)) 52 if (kstack_end(stack))
90 break; 53 break;
91 if (i && ((i % STACKSLOTS_PER_LINE) == 0)) 54 if (i && ((i % STACKSLOTS_PER_LINE) == 0))
92 printk(KERN_CONT "\n"); 55 pr_cont("\n");
93 printk(KERN_CONT " %08lx", *stack++); 56 pr_cont(" %08lx", *stack++);
94 } 57 }
95 printk(KERN_CONT "\n"); 58 pr_cont("\n");
96 59
97 do_stack_trace(sp, bp); 60 pr_info("Call Trace:\n");
61 dump_trace(current, &stackops, NULL);
62 pr_info("\n");
98} 63}