Commit | Line | Data |
---|---|---|
5bdc9b44 | 1 | /* |
5bdc9b44 HC |
2 | * Stack trace management functions |
3 | * | |
a53c8fab | 4 | * Copyright IBM Corp. 2006 |
5bdc9b44 HC |
5 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> |
6 | */ | |
7 | ||
8 | #include <linux/sched.h> | |
9 | #include <linux/stacktrace.h> | |
10 | #include <linux/kallsyms.h> | |
8de2ce86 | 11 | #include <linux/module.h> |
5bdc9b44 | 12 | |
4d284cac | 13 | static unsigned long save_context_stack(struct stack_trace *trace, |
4d284cac HC |
14 | unsigned long sp, |
15 | unsigned long low, | |
a3afe70b HC |
16 | unsigned long high, |
17 | int savesched) | |
5bdc9b44 HC |
18 | { |
19 | struct stack_frame *sf; | |
20 | struct pt_regs *regs; | |
21 | unsigned long addr; | |
22 | ||
23 | while(1) { | |
5bdc9b44 HC |
24 | if (sp < low || sp > high) |
25 | return sp; | |
26 | sf = (struct stack_frame *)sp; | |
27 | while(1) { | |
9cb1ccec | 28 | addr = sf->gprs[8]; |
e90a2857 | 29 | if (!trace->skip) |
5bdc9b44 HC |
30 | trace->entries[trace->nr_entries++] = addr; |
31 | else | |
e90a2857 | 32 | trace->skip--; |
5bdc9b44 HC |
33 | if (trace->nr_entries >= trace->max_entries) |
34 | return sp; | |
35 | low = sp; | |
9cb1ccec | 36 | sp = sf->back_chain; |
5bdc9b44 HC |
37 | if (!sp) |
38 | break; | |
39 | if (sp <= low || sp > high - sizeof(*sf)) | |
40 | return sp; | |
41 | sf = (struct stack_frame *)sp; | |
42 | } | |
43 | /* Zero backchain detected, check for interrupt frame. */ | |
44 | sp = (unsigned long)(sf + 1); | |
45 | if (sp <= low || sp > high - sizeof(*regs)) | |
46 | return sp; | |
47 | regs = (struct pt_regs *)sp; | |
9cb1ccec | 48 | addr = regs->psw.addr; |
a3afe70b HC |
49 | if (savesched || !in_sched_functions(addr)) { |
50 | if (!trace->skip) | |
51 | trace->entries[trace->nr_entries++] = addr; | |
52 | else | |
53 | trace->skip--; | |
54 | } | |
5bdc9b44 HC |
55 | if (trace->nr_entries >= trace->max_entries) |
56 | return sp; | |
57 | low = sp; | |
58 | sp = regs->gprs[15]; | |
59 | } | |
60 | } | |
61 | ||
66adce8f | 62 | static void __save_stack_trace(struct stack_trace *trace, unsigned long sp) |
5bdc9b44 | 63 | { |
66adce8f | 64 | unsigned long new_sp, frame_size; |
5bdc9b44 | 65 | |
9900c48c | 66 | frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); |
66adce8f | 67 | new_sp = save_context_stack(trace, sp, |
9900c48c HC |
68 | S390_lowcore.panic_stack + frame_size - PAGE_SIZE, |
69 | S390_lowcore.panic_stack + frame_size, 1); | |
e90a2857 | 70 | new_sp = save_context_stack(trace, new_sp, |
9900c48c HC |
71 | S390_lowcore.async_stack + frame_size - ASYNC_SIZE, |
72 | S390_lowcore.async_stack + frame_size, 1); | |
e90a2857 | 73 | save_context_stack(trace, new_sp, |
ab1b6f03 | 74 | S390_lowcore.thread_info, |
a3afe70b | 75 | S390_lowcore.thread_info + THREAD_SIZE, 1); |
66adce8f HC |
76 | } |
77 | ||
78 | void save_stack_trace(struct stack_trace *trace) | |
79 | { | |
80 | register unsigned long r15 asm ("15"); | |
81 | unsigned long sp; | |
82 | ||
83 | sp = r15; | |
84 | __save_stack_trace(trace, sp); | |
f6331aac HC |
85 | if (trace->nr_entries < trace->max_entries) |
86 | trace->entries[trace->nr_entries++] = ULONG_MAX; | |
a3afe70b | 87 | } |
7b4c9505 | 88 | EXPORT_SYMBOL_GPL(save_stack_trace); |
a3afe70b HC |
89 | |
90 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |
91 | { | |
92 | unsigned long sp, low, high; | |
93 | ||
9cb1ccec | 94 | sp = tsk->thread.ksp; |
665ca918 HC |
95 | if (tsk == current) { |
96 | /* Get current stack pointer. */ | |
97 | asm volatile("la %0,0(15)" : "=a" (sp)); | |
98 | } | |
a3afe70b HC |
99 | low = (unsigned long) task_stack_page(tsk); |
100 | high = (unsigned long) task_pt_regs(tsk); | |
101 | save_context_stack(trace, sp, low, high, 0); | |
102 | if (trace->nr_entries < trace->max_entries) | |
103 | trace->entries[trace->nr_entries++] = ULONG_MAX; | |
5bdc9b44 | 104 | } |
7b4c9505 | 105 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); |