Commit 2b14a78cd07a52001b8c3865ed615d8b9b905b78
Committed by
Andi Kleen
1 parent
be7a91709b
Exists in
master
and in
4 other branches
[PATCH] i386: Do stacktracer conversion too
Following x86-64 patches. Reuses code from them in fact. Convert the standard backtracer to do all output using callbacks. Use the x86-64 stack tracer implementation that uses these callbacks to implement the stacktrace interface. This allows to use the new dwarf2 unwinder for stacktrace and get better backtraces. Cc: mingo@elte.hu Signed-off-by: Andi Kleen <ak@suse.de>
Showing 4 changed files with 82 additions and 121 deletions Side-by-side Diff
arch/i386/kernel/Makefile
arch/i386/kernel/stacktrace.c
| 1 | -/* | |
| 2 | - * arch/i386/kernel/stacktrace.c | |
| 3 | - * | |
| 4 | - * Stack trace management functions | |
| 5 | - * | |
| 6 | - * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com> | |
| 7 | - */ | |
| 8 | -#include <linux/sched.h> | |
| 9 | -#include <linux/stacktrace.h> | |
| 10 | - | |
| 11 | -static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) | |
| 12 | -{ | |
| 13 | - return p > (void *)tinfo && | |
| 14 | - p < (void *)tinfo + THREAD_SIZE - 3; | |
| 15 | -} | |
| 16 | - | |
| 17 | -/* | |
| 18 | - * Save stack-backtrace addresses into a stack_trace buffer: | |
| 19 | - */ | |
| 20 | -static inline unsigned long | |
| 21 | -save_context_stack(struct stack_trace *trace, unsigned int skip, | |
| 22 | - struct thread_info *tinfo, unsigned long *stack, | |
| 23 | - unsigned long ebp) | |
| 24 | -{ | |
| 25 | - unsigned long addr; | |
| 26 | - | |
| 27 | -#ifdef CONFIG_FRAME_POINTER | |
| 28 | - while (valid_stack_ptr(tinfo, (void *)ebp)) { | |
| 29 | - addr = *(unsigned long *)(ebp + 4); | |
| 30 | - if (!skip) | |
| 31 | - trace->entries[trace->nr_entries++] = addr; | |
| 32 | - else | |
| 33 | - skip--; | |
| 34 | - if (trace->nr_entries >= trace->max_entries) | |
| 35 | - break; | |
| 36 | - /* | |
| 37 | - * break out of recursive entries (such as | |
| 38 | - * end_of_stack_stop_unwind_function): | |
| 39 | - */ | |
| 40 | - if (ebp == *(unsigned long *)ebp) | |
| 41 | - break; | |
| 42 | - | |
| 43 | - ebp = *(unsigned long *)ebp; | |
| 44 | - } | |
| 45 | -#else | |
| 46 | - while (valid_stack_ptr(tinfo, stack)) { | |
| 47 | - addr = *stack++; | |
| 48 | - if (__kernel_text_address(addr)) { | |
| 49 | - if (!skip) | |
| 50 | - trace->entries[trace->nr_entries++] = addr; | |
| 51 | - else | |
| 52 | - skip--; | |
| 53 | - if (trace->nr_entries >= trace->max_entries) | |
| 54 | - break; | |
| 55 | - } | |
| 56 | - } | |
| 57 | -#endif | |
| 58 | - | |
| 59 | - return ebp; | |
| 60 | -} | |
| 61 | - | |
| 62 | -/* | |
| 63 | - * Save stack-backtrace addresses into a stack_trace buffer. | |
| 64 | - */ | |
| 65 | -void save_stack_trace(struct stack_trace *trace, struct task_struct *task) | |
| 66 | -{ | |
| 67 | - unsigned long ebp; | |
| 68 | - unsigned long *stack = &ebp; | |
| 69 | - | |
| 70 | - WARN_ON(trace->nr_entries || !trace->max_entries); | |
| 71 | - | |
| 72 | - if (!task || task == current) { | |
| 73 | - /* Grab ebp right from our regs: */ | |
| 74 | - asm ("movl %%ebp, %0" : "=r" (ebp)); | |
| 75 | - } else { | |
| 76 | - /* ebp is the last reg pushed by switch_to(): */ | |
| 77 | - ebp = *(unsigned long *) task->thread.esp; | |
| 78 | - } | |
| 79 | - | |
| 80 | - while (1) { | |
| 81 | - struct thread_info *context = (struct thread_info *) | |
| 82 | - ((unsigned long)stack & (~(THREAD_SIZE - 1))); | |
| 83 | - | |
| 84 | - ebp = save_context_stack(trace, trace->skip, context, stack, ebp); | |
| 85 | - stack = (unsigned long *)context->previous_esp; | |
| 86 | - if (!stack || trace->nr_entries >= trace->max_entries) | |
| 87 | - break; | |
| 88 | - trace->entries[trace->nr_entries++] = ULONG_MAX; | |
| 89 | - if (trace->nr_entries >= trace->max_entries) | |
| 90 | - break; | |
| 91 | - } | |
| 92 | -} |
arch/i386/kernel/traps.c
| ... | ... | @@ -51,6 +51,7 @@ |
| 51 | 51 | #include <asm/smp.h> |
| 52 | 52 | #include <asm/arch_hooks.h> |
| 53 | 53 | #include <asm/kdebug.h> |
| 54 | +#include <asm/stacktrace.h> | |
| 54 | 55 | |
| 55 | 56 | #include <linux/module.h> |
| 56 | 57 | |
| 57 | 58 | |
| 58 | 59 | |
| ... | ... | @@ -118,26 +119,16 @@ |
| 118 | 119 | p < (void *)tinfo + THREAD_SIZE - 3; |
| 119 | 120 | } |
| 120 | 121 | |
| 121 | -/* | |
| 122 | - * Print one address/symbol entries per line. | |
| 123 | - */ | |
| 124 | -static inline void print_addr_and_symbol(unsigned long addr, char *log_lvl) | |
| 125 | -{ | |
| 126 | - printk(" [<%08lx>] ", addr); | |
| 127 | - | |
| 128 | - print_symbol("%s\n", addr); | |
| 129 | -} | |
| 130 | - | |
| 131 | 122 | static inline unsigned long print_context_stack(struct thread_info *tinfo, |
| 132 | 123 | unsigned long *stack, unsigned long ebp, |
| 133 | - char *log_lvl) | |
| 124 | + struct stacktrace_ops *ops, void *data) | |
| 134 | 125 | { |
| 135 | 126 | unsigned long addr; |
| 136 | 127 | |
| 137 | 128 | #ifdef CONFIG_FRAME_POINTER |
| 138 | 129 | while (valid_stack_ptr(tinfo, (void *)ebp)) { |
| 139 | 130 | addr = *(unsigned long *)(ebp + 4); |
| 140 | - print_addr_and_symbol(addr, log_lvl); | |
| 131 | + ops->address(data, addr); | |
| 141 | 132 | /* |
| 142 | 133 | * break out of recursive entries (such as |
| 143 | 134 | * end_of_stack_stop_unwind_function): |
| 144 | 135 | |
| 145 | 136 | |
| 146 | 137 | |
| 147 | 138 | |
| 148 | 139 | |
| ... | ... | @@ -150,28 +141,35 @@ |
| 150 | 141 | while (valid_stack_ptr(tinfo, stack)) { |
| 151 | 142 | addr = *stack++; |
| 152 | 143 | if (__kernel_text_address(addr)) |
| 153 | - print_addr_and_symbol(addr, log_lvl); | |
| 144 | + ops->address(data, addr); | |
| 154 | 145 | } |
| 155 | 146 | #endif |
| 156 | 147 | return ebp; |
| 157 | 148 | } |
| 158 | 149 | |
| 150 | +struct ops_and_data { | |
| 151 | + struct stacktrace_ops *ops; | |
| 152 | + void *data; | |
| 153 | +}; | |
| 154 | + | |
| 159 | 155 | static asmlinkage int |
| 160 | -show_trace_unwind(struct unwind_frame_info *info, void *log_lvl) | |
| 156 | +dump_trace_unwind(struct unwind_frame_info *info, void *data) | |
| 161 | 157 | { |
| 158 | + struct ops_and_data *oad = (struct ops_and_data *)data; | |
| 162 | 159 | int n = 0; |
| 163 | 160 | |
| 164 | 161 | while (unwind(info) == 0 && UNW_PC(info)) { |
| 165 | 162 | n++; |
| 166 | - print_addr_and_symbol(UNW_PC(info), log_lvl); | |
| 163 | + oad->ops->address(oad->data, UNW_PC(info)); | |
| 167 | 164 | if (arch_unw_user_mode(info)) |
| 168 | 165 | break; |
| 169 | 166 | } |
| 170 | 167 | return n; |
| 171 | 168 | } |
| 172 | 169 | |
| 173 | -static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |
| 174 | - unsigned long *stack, char *log_lvl) | |
| 170 | +void dump_trace(struct task_struct *task, struct pt_regs *regs, | |
| 171 | + unsigned long *stack, | |
| 172 | + struct stacktrace_ops *ops, void *data) | |
| 175 | 173 | { |
| 176 | 174 | unsigned long ebp; |
| 177 | 175 | |
| 178 | 176 | |
| 179 | 177 | |
| 180 | 178 | |
| 181 | 179 | |
| 182 | 180 | |
| 183 | 181 | |
| 184 | 182 | |
| 185 | 183 | |
| ... | ... | @@ -181,31 +179,37 @@ |
| 181 | 179 | if (call_trace >= 0) { |
| 182 | 180 | int unw_ret = 0; |
| 183 | 181 | struct unwind_frame_info info; |
| 182 | + struct ops_and_data oad = { .ops = ops, .data = data }; | |
| 184 | 183 | |
| 185 | 184 | if (regs) { |
| 186 | 185 | if (unwind_init_frame_info(&info, task, regs) == 0) |
| 187 | - unw_ret = show_trace_unwind(&info, log_lvl); | |
| 186 | + unw_ret = dump_trace_unwind(&info, &oad); | |
| 188 | 187 | } else if (task == current) |
| 189 | - unw_ret = unwind_init_running(&info, show_trace_unwind, log_lvl); | |
| 188 | + unw_ret = unwind_init_running(&info, dump_trace_unwind, &oad); | |
| 190 | 189 | else { |
| 191 | 190 | if (unwind_init_blocked(&info, task) == 0) |
| 192 | - unw_ret = show_trace_unwind(&info, log_lvl); | |
| 191 | + unw_ret = dump_trace_unwind(&info, &oad); | |
| 193 | 192 | } |
| 194 | 193 | if (unw_ret > 0) { |
| 195 | 194 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { |
| 196 | - print_symbol("DWARF2 unwinder stuck at %s\n", | |
| 195 | + ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n", | |
| 197 | 196 | UNW_PC(&info)); |
| 198 | 197 | if (UNW_SP(&info) >= PAGE_OFFSET) { |
| 199 | - printk("Leftover inexact backtrace:\n"); | |
| 198 | + ops->warning(data, "Leftover inexact backtrace:\n"); | |
| 200 | 199 | stack = (void *)UNW_SP(&info); |
| 201 | 200 | } else |
| 202 | - printk("Full inexact backtrace again:\n"); | |
| 201 | + ops->warning(data, "Full inexact backtrace again:\n"); | |
| 203 | 202 | } else if (call_trace >= 1) |
| 204 | 203 | return; |
| 205 | 204 | else |
| 206 | - printk("Full inexact backtrace again:\n"); | |
| 205 | + ops->warning(data, "Full inexact backtrace again:\n"); | |
| 207 | 206 | } else |
| 208 | - printk("Inexact backtrace:\n"); | |
| 207 | + ops->warning(data, "Inexact backtrace:\n"); | |
| 208 | + } else if (!stack) { | |
| 209 | + unsigned long dummy; | |
| 210 | + stack = &dummy; | |
| 211 | + if (task && task != current) | |
| 212 | + stack = (unsigned long *)task->thread.esp; | |
| 209 | 213 | } |
| 210 | 214 | |
| 211 | 215 | if (task == current) { |
| 212 | 216 | |
| 213 | 217 | |
| 214 | 218 | |
| ... | ... | @@ -220,15 +224,63 @@ |
| 220 | 224 | struct thread_info *context; |
| 221 | 225 | context = (struct thread_info *) |
| 222 | 226 | ((unsigned long)stack & (~(THREAD_SIZE - 1))); |
| 223 | - ebp = print_context_stack(context, stack, ebp, log_lvl); | |
| 227 | + ebp = print_context_stack(context, stack, ebp, ops, data); | |
| 228 | + /* Should be after the line below, but somewhere | |
| 229 | + in early boot context comes out corrupted and we | |
| 230 | + can't reference it -AK */ | |
| 231 | + if (ops->stack(data, "IRQ") < 0) | |
| 232 | + break; | |
| 224 | 233 | stack = (unsigned long*)context->previous_esp; |
| 225 | 234 | if (!stack) |
| 226 | 235 | break; |
| 227 | - printk("%s =======================\n", log_lvl); | |
| 228 | 236 | } |
| 229 | 237 | } |
| 238 | +EXPORT_SYMBOL(dump_trace); | |
| 230 | 239 | |
| 231 | -void show_trace(struct task_struct *task, struct pt_regs *regs, unsigned long * stack) | |
| 240 | +static void | |
| 241 | +print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) | |
| 242 | +{ | |
| 243 | + printk(data); | |
| 244 | + print_symbol(msg, symbol); | |
| 245 | + printk("\n"); | |
| 246 | +} | |
| 247 | + | |
| 248 | +static void print_trace_warning(void *data, char *msg) | |
| 249 | +{ | |
| 250 | + printk("%s%s\n", (char *)data, msg); | |
| 251 | +} | |
| 252 | + | |
| 253 | +static int print_trace_stack(void *data, char *name) | |
| 254 | +{ | |
| 255 | + return 0; | |
| 256 | +} | |
| 257 | + | |
| 258 | +/* | |
| 259 | + * Print one address/symbol entries per line. | |
| 260 | + */ | |
| 261 | +static void print_trace_address(void *data, unsigned long addr) | |
| 262 | +{ | |
| 263 | + printk("%s [<%08lx>] ", (char *)data, addr); | |
| 264 | + print_symbol("%s\n", addr); | |
| 265 | +} | |
| 266 | + | |
| 267 | +static struct stacktrace_ops print_trace_ops = { | |
| 268 | + .warning = print_trace_warning, | |
| 269 | + .warning_symbol = print_trace_warning_symbol, | |
| 270 | + .stack = print_trace_stack, | |
| 271 | + .address = print_trace_address, | |
| 272 | +}; | |
| 273 | + | |
| 274 | +static void | |
| 275 | +show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |
| 276 | + unsigned long * stack, char *log_lvl) | |
| 277 | +{ | |
| 278 | + dump_trace(task, regs, stack, &print_trace_ops, log_lvl); | |
| 279 | + printk("%s =======================\n", log_lvl); | |
| 280 | +} | |
| 281 | + | |
| 282 | +void show_trace(struct task_struct *task, struct pt_regs *regs, | |
| 283 | + unsigned long * stack) | |
| 232 | 284 | { |
| 233 | 285 | show_trace_log_lvl(task, regs, stack, ""); |
| 234 | 286 | } |
include/asm-i386/stacktrace.h
| 1 | +#include <asm-x86_64/stacktrace.h> |