diff options
author | Atsushi Nemoto <anemo@mba.ocn.ne.jp> | 2006-07-29 10:27:20 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2006-09-27 08:37:06 -0400 |
commit | f66686f70a2a61e53ee8c2284f75ca342e4c0dc8 (patch) | |
tree | 881e98d7255770314dde60109a316cf9f84522b2 /arch | |
parent | 79495d876c2074af5552a0c4b7aea600c2320e83 (diff) |
[MIPS] dump_stack() based on prologue code analysis
Instead of dump all possible address in the stack, unwind the stack frame
based on prologue code analysis, as like as get_wchan() does. While the
code analysis might fail for some reason, there is a new kernel option
"raw_show_trace" to disable this feature.
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/mips/kernel/process.c | 66 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 98 |
2 files changed, 141 insertions, 23 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 7ab67f786bfe..8709a46a45c1 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
@@ -281,7 +281,7 @@ static struct mips_frame_info { | |||
281 | } *schedule_frame, mfinfo[64]; | 281 | } *schedule_frame, mfinfo[64]; |
282 | static int mfinfo_num; | 282 | static int mfinfo_num; |
283 | 283 | ||
284 | static int __init get_frame_info(struct mips_frame_info *info) | 284 | static int get_frame_info(struct mips_frame_info *info) |
285 | { | 285 | { |
286 | int i; | 286 | int i; |
287 | void *func = info->func; | 287 | void *func = info->func; |
@@ -329,14 +329,12 @@ static int __init get_frame_info(struct mips_frame_info *info) | |||
329 | ip->i_format.simmediate / sizeof(long); | 329 | ip->i_format.simmediate / sizeof(long); |
330 | } | 330 | } |
331 | } | 331 | } |
332 | if (info->pc_offset == -1 || info->frame_size == 0) { | 332 | if (info->frame_size && info->pc_offset >= 0) /* nested */ |
333 | if (func == schedule) | 333 | return 0; |
334 | printk("Can't analyze prologue code at %p\n", func); | 334 | if (info->pc_offset < 0) /* leaf */ |
335 | info->pc_offset = -1; | 335 | return 1; |
336 | info->frame_size = 0; | 336 | /* prologue seems boggus... */ |
337 | } | 337 | return -1; |
338 | |||
339 | return 0; | ||
340 | } | 338 | } |
341 | 339 | ||
342 | static int __init frame_info_init(void) | 340 | static int __init frame_info_init(void) |
@@ -367,8 +365,15 @@ static int __init frame_info_init(void) | |||
367 | mfinfo[0].func = schedule; | 365 | mfinfo[0].func = schedule; |
368 | schedule_frame = &mfinfo[0]; | 366 | schedule_frame = &mfinfo[0]; |
369 | #endif | 367 | #endif |
370 | for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) | 368 | for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) { |
371 | get_frame_info(&mfinfo[i]); | 369 | struct mips_frame_info *info = &mfinfo[i]; |
370 | if (get_frame_info(info)) { | ||
371 | /* leaf or unknown */ | ||
372 | if (info->func == schedule) | ||
373 | printk("Can't analyze prologue code at %p\n", | ||
374 | info->func); | ||
375 | } | ||
376 | } | ||
372 | 377 | ||
373 | mfinfo_num = i; | 378 | mfinfo_num = i; |
374 | return 0; | 379 | return 0; |
@@ -427,6 +432,8 @@ unsigned long get_wchan(struct task_struct *p) | |||
427 | if (i < 0) | 432 | if (i < 0) |
428 | break; | 433 | break; |
429 | 434 | ||
435 | if (mfinfo[i].pc_offset < 0) | ||
436 | break; | ||
430 | pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; | 437 | pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; |
431 | if (!mfinfo[i].frame_size) | 438 | if (!mfinfo[i].frame_size) |
432 | break; | 439 | break; |
@@ -437,3 +444,40 @@ unsigned long get_wchan(struct task_struct *p) | |||
437 | return pc; | 444 | return pc; |
438 | } | 445 | } |
439 | 446 | ||
447 | #ifdef CONFIG_KALLSYMS | ||
448 | /* used by show_frametrace() */ | ||
449 | unsigned long unwind_stack(struct task_struct *task, | ||
450 | unsigned long **sp, unsigned long pc) | ||
451 | { | ||
452 | unsigned long stack_page; | ||
453 | struct mips_frame_info info; | ||
454 | char *modname; | ||
455 | char namebuf[KSYM_NAME_LEN + 1]; | ||
456 | unsigned long size, ofs; | ||
457 | |||
458 | stack_page = (unsigned long)task_stack_page(task); | ||
459 | if (!stack_page) | ||
460 | return 0; | ||
461 | |||
462 | if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf)) | ||
463 | return 0; | ||
464 | if (ofs == 0) | ||
465 | return 0; | ||
466 | |||
467 | info.func = (void *)(pc - ofs); | ||
468 | info.func_size = ofs; /* analyze from start to ofs */ | ||
469 | if (get_frame_info(&info)) { | ||
470 | /* leaf or unknown */ | ||
471 | *sp += info.frame_size / sizeof(long); | ||
472 | return 0; | ||
473 | } | ||
474 | if ((unsigned long)*sp < stack_page || | ||
475 | (unsigned long)*sp + info.frame_size / sizeof(long) > | ||
476 | stack_page + THREAD_SIZE - 32) | ||
477 | return 0; | ||
478 | |||
479 | pc = (*sp)[info.pc_offset]; | ||
480 | *sp += info.frame_size / sizeof(long); | ||
481 | return pc; | ||
482 | } | ||
483 | #endif | ||
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index c6f70467e750..7aa9dfc57b81 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -98,24 +98,53 @@ static void show_trace(unsigned long *stack) | |||
98 | printk("\n"); | 98 | printk("\n"); |
99 | } | 99 | } |
100 | 100 | ||
101 | #ifdef CONFIG_KALLSYMS | ||
102 | static int raw_show_trace; | ||
103 | static int __init set_raw_show_trace(char *str) | ||
104 | { | ||
105 | raw_show_trace = 1; | ||
106 | return 1; | ||
107 | } | ||
108 | __setup("raw_show_trace", set_raw_show_trace); | ||
109 | |||
110 | extern unsigned long unwind_stack(struct task_struct *task, | ||
111 | unsigned long **sp, unsigned long pc); | ||
112 | static void show_frametrace(struct task_struct *task, struct pt_regs *regs) | ||
113 | { | ||
114 | const int field = 2 * sizeof(unsigned long); | ||
115 | unsigned long *stack = (long *)regs->regs[29]; | ||
116 | unsigned long pc = regs->cp0_epc; | ||
117 | int top = 1; | ||
118 | |||
119 | if (raw_show_trace || !__kernel_text_address(pc)) { | ||
120 | show_trace(stack); | ||
121 | return; | ||
122 | } | ||
123 | printk("Call Trace:\n"); | ||
124 | while (__kernel_text_address(pc)) { | ||
125 | printk(" [<%0*lx>] ", field, pc); | ||
126 | print_symbol("%s\n", pc); | ||
127 | pc = unwind_stack(task, &stack, pc); | ||
128 | if (top && pc == 0) | ||
129 | pc = regs->regs[31]; /* leaf? */ | ||
130 | top = 0; | ||
131 | } | ||
132 | printk("\n"); | ||
133 | } | ||
134 | #else | ||
135 | #define show_frametrace(task, r) show_trace((long *)(r)->regs[29]); | ||
136 | #endif | ||
137 | |||
101 | /* | 138 | /* |
102 | * This routine abuses get_user()/put_user() to reference pointers | 139 | * This routine abuses get_user()/put_user() to reference pointers |
103 | * with at least a bit of error checking ... | 140 | * with at least a bit of error checking ... |
104 | */ | 141 | */ |
105 | void show_stack(struct task_struct *task, unsigned long *sp) | 142 | static void show_stacktrace(struct task_struct *task, struct pt_regs *regs) |
106 | { | 143 | { |
107 | const int field = 2 * sizeof(unsigned long); | 144 | const int field = 2 * sizeof(unsigned long); |
108 | long stackdata; | 145 | long stackdata; |
109 | int i; | 146 | int i; |
110 | unsigned long *stack; | 147 | unsigned long *sp = (unsigned long *)regs->regs[29]; |
111 | |||
112 | if (!sp) { | ||
113 | if (task && task != current) | ||
114 | sp = (unsigned long *) task->thread.reg29; | ||
115 | else | ||
116 | sp = (unsigned long *) &sp; | ||
117 | } | ||
118 | stack = sp; | ||
119 | 148 | ||
120 | printk("Stack :"); | 149 | printk("Stack :"); |
121 | i = 0; | 150 | i = 0; |
@@ -136,7 +165,44 @@ void show_stack(struct task_struct *task, unsigned long *sp) | |||
136 | i++; | 165 | i++; |
137 | } | 166 | } |
138 | printk("\n"); | 167 | printk("\n"); |
139 | show_trace(stack); | 168 | show_frametrace(task, regs); |
169 | } | ||
170 | |||
171 | static noinline void prepare_frametrace(struct pt_regs *regs) | ||
172 | { | ||
173 | __asm__ __volatile__( | ||
174 | "1: la $2, 1b\n\t" | ||
175 | #ifdef CONFIG_64BIT | ||
176 | "sd $2, %0\n\t" | ||
177 | "sd $29, %1\n\t" | ||
178 | "sd $31, %2\n\t" | ||
179 | #else | ||
180 | "sw $2, %0\n\t" | ||
181 | "sw $29, %1\n\t" | ||
182 | "sw $31, %2\n\t" | ||
183 | #endif | ||
184 | : "=m" (regs->cp0_epc), | ||
185 | "=m" (regs->regs[29]), "=m" (regs->regs[31]) | ||
186 | : : "memory"); | ||
187 | } | ||
188 | |||
189 | void show_stack(struct task_struct *task, unsigned long *sp) | ||
190 | { | ||
191 | struct pt_regs regs; | ||
192 | if (sp) { | ||
193 | regs.regs[29] = (unsigned long)sp; | ||
194 | regs.regs[31] = 0; | ||
195 | regs.cp0_epc = 0; | ||
196 | } else { | ||
197 | if (task && task != current) { | ||
198 | regs.regs[29] = task->thread.reg29; | ||
199 | regs.regs[31] = 0; | ||
200 | regs.cp0_epc = task->thread.reg31; | ||
201 | } else { | ||
202 | prepare_frametrace(®s); | ||
203 | } | ||
204 | } | ||
205 | show_stacktrace(task, ®s); | ||
140 | } | 206 | } |
141 | 207 | ||
142 | /* | 208 | /* |
@@ -146,6 +212,14 @@ void dump_stack(void) | |||
146 | { | 212 | { |
147 | unsigned long stack; | 213 | unsigned long stack; |
148 | 214 | ||
215 | #ifdef CONFIG_KALLSYMS | ||
216 | if (!raw_show_trace) { | ||
217 | struct pt_regs regs; | ||
218 | prepare_frametrace(®s); | ||
219 | show_frametrace(current, ®s); | ||
220 | return; | ||
221 | } | ||
222 | #endif | ||
149 | show_trace(&stack); | 223 | show_trace(&stack); |
150 | } | 224 | } |
151 | 225 | ||
@@ -265,7 +339,7 @@ void show_registers(struct pt_regs *regs) | |||
265 | print_modules(); | 339 | print_modules(); |
266 | printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", | 340 | printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", |
267 | current->comm, current->pid, current_thread_info(), current); | 341 | current->comm, current->pid, current_thread_info(), current); |
268 | show_stack(current, (long *) regs->regs[29]); | 342 | show_stacktrace(current, regs); |
269 | show_code((unsigned int *) regs->cp0_epc); | 343 | show_code((unsigned int *) regs->cp0_epc); |
270 | printk("\n"); | 344 | printk("\n"); |
271 | } | 345 | } |