1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 1991, 1992 Linus Torvalds |
4 | * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs |
5 | * Copyright (C) 2009 Matt Fleming |
6 | * Copyright (C) 2002 - 2012 Paul Mundt |
7 | */ |
8 | #include <linux/kallsyms.h> |
9 | #include <linux/ftrace.h> |
10 | #include <linux/debug_locks.h> |
11 | #include <linux/sched/debug.h> |
12 | #include <linux/sched/task_stack.h> |
13 | #include <linux/kdebug.h> |
14 | #include <linux/export.h> |
15 | #include <linux/uaccess.h> |
16 | #include <asm/unwinder.h> |
17 | #include <asm/stacktrace.h> |
18 | |
19 | void dump_mem(const char *str, const char *loglvl, unsigned long bottom, |
20 | unsigned long top) |
21 | { |
22 | unsigned long p; |
23 | int i; |
24 | |
25 | printk("%s%s(0x%08lx to 0x%08lx)\n" , loglvl, str, bottom, top); |
26 | |
27 | for (p = bottom & ~31; p < top; ) { |
28 | printk("%s%04lx: " , loglvl, p & 0xffff); |
29 | |
30 | for (i = 0; i < 8; i++, p += 4) { |
31 | unsigned int val; |
32 | |
33 | if (p < bottom || p >= top) |
34 | pr_cont(" " ); |
35 | else { |
36 | if (__get_user(val, (unsigned int __user *)p)) { |
37 | pr_cont("\n" ); |
38 | return; |
39 | } |
40 | pr_cont("%08x " , val); |
41 | } |
42 | } |
43 | pr_cont("\n" ); |
44 | } |
45 | } |
46 | |
47 | void printk_address(unsigned long address, int reliable) |
48 | { |
49 | pr_cont(" [<%px>] %s%pS\n" , (void *) address, |
50 | reliable ? "" : "? " , (void *) address); |
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; |
60 | struct ftrace_ret_stack *ret_stack; |
61 | unsigned long ret_addr; |
62 | |
63 | if (addr != (unsigned long)return_to_handler) |
64 | return; |
65 | |
66 | if (!task->ret_stack) |
67 | return; |
68 | |
69 | ret_stack = ftrace_graph_get_ret_stack(task, idx: *graph); |
70 | if (!ret_stack) |
71 | return; |
72 | |
73 | ret_addr = ret_stack->ret; |
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 | |
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) |
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(addr: sp)) { |
99 | unsigned long addr = *sp++; |
100 | |
101 | if (__kernel_text_address(addr)) { |
102 | ops->address(data, addr, 1); |
103 | |
104 | print_ftrace_graph_addr(addr, data, ops, |
105 | tinfo: context, graph: &graph); |
106 | } |
107 | } |
108 | } |
109 | |
110 | /* |
111 | * Print one address/symbol entries per line. |
112 | */ |
113 | static void print_trace_address(void *data, unsigned long addr, int reliable) |
114 | { |
115 | printk("%s" , (char *)data); |
116 | printk_address(address: addr, reliable); |
117 | } |
118 | |
119 | static const struct stacktrace_ops print_trace_ops = { |
120 | .address = print_trace_address, |
121 | }; |
122 | |
123 | void show_trace(struct task_struct *tsk, unsigned long *sp, |
124 | struct pt_regs *regs, const char *loglvl) |
125 | { |
126 | if (regs && user_mode(regs)) |
127 | return; |
128 | |
129 | printk("%s\nCall trace:\n" , loglvl); |
130 | |
131 | unwind_stack(tsk, regs, sp, &print_trace_ops, (void *)loglvl); |
132 | |
133 | pr_cont("\n" ); |
134 | |
135 | if (!tsk) |
136 | tsk = current; |
137 | |
138 | debug_show_held_locks(task: tsk); |
139 | } |
140 | |
141 | void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl) |
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; |
153 | dump_mem(str: "Stack: " , loglvl, bottom: stack, THREAD_SIZE + |
154 | (unsigned long)task_stack_page(task: tsk)); |
155 | show_trace(tsk, sp, NULL, loglvl); |
156 | } |
157 | |