Commit | Line | Data |
---|---|---|
5933f6d2 | 1 | // SPDX-License-Identifier: GPL-2.0 |
4e14dfc7 MF |
2 | /* |
3 | * Copyright (C) 1991, 1992 Linus Torvalds | |
4 | * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs | |
5 | * Copyright (C) 2009 Matt Fleming | |
49453264 | 6 | * Copyright (C) 2002 - 2012 Paul Mundt |
4e14dfc7 MF |
7 | */ |
8 | #include <linux/kallsyms.h> | |
9 | #include <linux/ftrace.h> | |
10 | #include <linux/debug_locks.h> | |
b17b0153 | 11 | #include <linux/sched/debug.h> |
68db0cf1 | 12 | #include <linux/sched/task_stack.h> |
49453264 PM |
13 | #include <linux/kdebug.h> |
14 | #include <linux/export.h> | |
15 | #include <linux/uaccess.h> | |
0eff9f66 | 16 | #include <asm/unwinder.h> |
4e14dfc7 MF |
17 | #include <asm/stacktrace.h> |
18 | ||
0632a6d8 GU |
19 | void dump_mem(const char *str, const char *loglvl, unsigned long bottom, |
20 | unsigned long top) | |
49453264 PM |
21 | { |
22 | unsigned long p; | |
23 | int i; | |
24 | ||
ebf0a36a | 25 | printk("%s%s(0x%08lx to 0x%08lx)\n", loglvl, str, bottom, top); |
49453264 PM |
26 | |
27 | for (p = bottom & ~31; p < top; ) { | |
ebf0a36a | 28 | printk("%s%04lx: ", loglvl, p & 0xffff); |
49453264 PM |
29 | |
30 | for (i = 0; i < 8; i++, p += 4) { | |
31 | unsigned int val; | |
32 | ||
33 | if (p < bottom || p >= top) | |
0632a6d8 | 34 | pr_cont(" "); |
49453264 PM |
35 | else { |
36 | if (__get_user(val, (unsigned int __user *)p)) { | |
0632a6d8 | 37 | pr_cont("\n"); |
49453264 PM |
38 | return; |
39 | } | |
0632a6d8 | 40 | pr_cont("%08x ", val); |
49453264 PM |
41 | } |
42 | } | |
0632a6d8 | 43 | pr_cont("\n"); |
49453264 PM |
44 | } |
45 | } | |
46 | ||
fd722f25 | 47 | void printk_address(unsigned long address, int reliable) |
4e14dfc7 | 48 | { |
0632a6d8 GU |
49 | pr_cont(" [<%px>] %s%pS\n", (void *) address, |
50 | reliable ? "" : "? ", (void *) address); | |
4e14dfc7 MF |
51 | } |
52 | ||
53 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
54 | static void | |
55 | print_ftrace_graph_addr(unsigned long addr, void *data, | |
56 | const struct stacktrace_ops *ops, | |
57 | struct thread_info *tinfo, int *graph) | |
58 | { | |
59 | struct task_struct *task = tinfo->task; | |
cec8d0e7 | 60 | struct ftrace_ret_stack *ret_stack; |
4e14dfc7 | 61 | unsigned long ret_addr; |
4e14dfc7 MF |
62 | |
63 | if (addr != (unsigned long)return_to_handler) | |
64 | return; | |
65 | ||
cec8d0e7 | 66 | if (!task->ret_stack) |
4e14dfc7 MF |
67 | return; |
68 | ||
cec8d0e7 SRV |
69 | ret_stack = ftrace_graph_get_ret_stack(task, *graph); |
70 | if (!ret_stack) | |
71 | return; | |
72 | ||
73 | ret_addr = ret_stack->ret; | |
4e14dfc7 MF |
74 | |
75 | ops->address(data, ret_addr, 1); | |
76 | ||
77 | (*graph)++; | |
78 | } | |
79 | #else | |
80 | static inline void | |
81 | print_ftrace_graph_addr(unsigned long addr, void *data, | |
82 | const struct stacktrace_ops *ops, | |
83 | struct thread_info *tinfo, int *graph) | |
84 | { } | |
85 | #endif | |
86 | ||
0eff9f66 MF |
87 | void |
88 | stack_reader_dump(struct task_struct *task, struct pt_regs *regs, | |
89 | unsigned long *sp, const struct stacktrace_ops *ops, | |
90 | void *data) | |
4e14dfc7 MF |
91 | { |
92 | struct thread_info *context; | |
93 | int graph = 0; | |
94 | ||
95 | context = (struct thread_info *) | |
96 | ((unsigned long)sp & (~(THREAD_SIZE - 1))); | |
97 | ||
98 | while (!kstack_end(sp)) { | |
99 | unsigned long addr = *sp++; | |
100 | ||
101 | if (__kernel_text_address(addr)) { | |
f9967e23 | 102 | ops->address(data, addr, 1); |
4e14dfc7 MF |
103 | |
104 | print_ftrace_graph_addr(addr, data, ops, | |
105 | context, &graph); | |
106 | } | |
107 | } | |
108 | } | |
4e14dfc7 | 109 | |
4e14dfc7 MF |
110 | /* |
111 | * Print one address/symbol entries per line. | |
112 | */ | |
113 | static void print_trace_address(void *data, unsigned long addr, int reliable) | |
114 | { | |
f6bed866 | 115 | printk("%s", (char *)data); |
fd722f25 | 116 | printk_address(addr, reliable); |
4e14dfc7 MF |
117 | } |
118 | ||
119 | static const struct stacktrace_ops print_trace_ops = { | |
4e14dfc7 MF |
120 | .address = print_trace_address, |
121 | }; | |
122 | ||
123 | void show_trace(struct task_struct *tsk, unsigned long *sp, | |
539e786c | 124 | struct pt_regs *regs, const char *loglvl) |
4e14dfc7 MF |
125 | { |
126 | if (regs && user_mode(regs)) | |
127 | return; | |
128 | ||
539e786c | 129 | printk("%s\nCall trace:\n", loglvl); |
4e14dfc7 | 130 | |
539e786c | 131 | unwind_stack(tsk, regs, sp, &print_trace_ops, (void *)loglvl); |
4e14dfc7 | 132 | |
0632a6d8 | 133 | pr_cont("\n"); |
4e14dfc7 MF |
134 | |
135 | if (!tsk) | |
136 | tsk = current; | |
137 | ||
138 | debug_show_held_locks(tsk); | |
139 | } | |
49453264 | 140 | |
9cb8f069 | 141 | void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl) |
49453264 PM |
142 | { |
143 | unsigned long stack; | |
144 | ||
145 | if (!tsk) | |
146 | tsk = current; | |
147 | if (tsk == current) | |
148 | sp = (unsigned long *)current_stack_pointer; | |
149 | else | |
150 | sp = (unsigned long *)tsk->thread.sp; | |
151 | ||
152 | stack = (unsigned long)sp; | |
e6e371c4 | 153 | dump_mem("Stack: ", loglvl, stack, THREAD_SIZE + |
49453264 | 154 | (unsigned long)task_stack_page(tsk)); |
e6e371c4 DS |
155 | show_trace(tsk, sp, NULL, loglvl); |
156 | } |