diff options
author | Daniel Walter <dwalter@google.com> | 2014-08-20 05:56:00 -0400 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2014-10-13 15:46:25 -0400 |
commit | 970e51feaddbc33ed0e7d187af7f69d1a12c7b6a (patch) | |
tree | dbe47fda5a1271dc5404c9c96d7965a870320fc2 /arch/um | |
parent | 2a2361228c5e6d8c1733f00653481de918598e50 (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.common | 3 | ||||
-rw-r--r-- | arch/um/include/asm/stacktrace.h | 42 | ||||
-rw-r--r-- | arch/um/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/um/kernel/stacktrace.c | 80 | ||||
-rw-r--r-- | arch/um/kernel/sysrq.c | 69 |
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 | ||
40 | config STACKTRACE_SUPPORT | 40 | config STACKTRACE_SUPPORT |
41 | bool | 41 | bool |
42 | default n | 42 | default y |
43 | select STACKTRACE | ||
43 | 44 | ||
44 | config GENERIC_CALIBRATE_DELAY | 45 | config 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 | |||
7 | struct stack_frame { | ||
8 | struct stack_frame *next_frame; | ||
9 | unsigned long return_address; | ||
10 | }; | ||
11 | |||
12 | struct stacktrace_ops { | ||
13 | void (*address)(void *data, unsigned long address, int reliable); | ||
14 | }; | ||
15 | |||
16 | #ifdef CONFIG_FRAME_POINTER | ||
17 | static inline unsigned long | ||
18 | get_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 | ||
25 | static inline unsigned long | ||
26 | get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs) | ||
27 | { | ||
28 | return 0; | ||
29 | } | ||
30 | #endif | ||
31 | |||
32 | static 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 | |||
40 | void 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 | |||
19 | obj-$(CONFIG_GPROF) += gprof_syms.o | 19 | obj-$(CONFIG_GPROF) += gprof_syms.o |
20 | obj-$(CONFIG_GCOV) += gmon_syms.o | 20 | obj-$(CONFIG_GCOV) += gmon_syms.o |
21 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | 21 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o |
22 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | ||
22 | 23 | ||
23 | USER_OBJS := config.o | 24 | USER_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 | |||
19 | void 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 | |||
47 | static 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 | |||
59 | static const struct stacktrace_ops dump_ops = { | ||
60 | .address = save_addr | ||
61 | }; | ||
62 | |||
63 | static 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 | |||
70 | void save_stack_trace(struct stack_trace *trace) | ||
71 | { | ||
72 | __save_stack_trace(current, trace); | ||
73 | } | ||
74 | EXPORT_SYMBOL_GPL(save_stack_trace); | ||
75 | |||
76 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | ||
77 | { | ||
78 | __save_stack_trace(tsk, trace); | ||
79 | } | ||
80 | EXPORT_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 | ||
17 | struct stack_frame { | 18 | static void _print_addr(void *data, unsigned long address, int reliable) |
18 | struct stack_frame *next_frame; | ||
19 | unsigned long return_address; | ||
20 | }; | ||
21 | |||
22 | static 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 | ||
49 | static unsigned long get_frame_pointer(struct task_struct *task, | 26 | static 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 | |||
58 | static 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 | ||
67 | void show_stack(struct task_struct *task, unsigned long *stack) | 30 | void 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 | } |