[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>
This commit is contained in:
Родитель
79495d876c
Коммит
f66686f70a
|
@ -281,7 +281,7 @@ static struct mips_frame_info {
|
||||||
} *schedule_frame, mfinfo[64];
|
} *schedule_frame, mfinfo[64];
|
||||||
static int mfinfo_num;
|
static int mfinfo_num;
|
||||||
|
|
||||||
static int __init get_frame_info(struct mips_frame_info *info)
|
static int get_frame_info(struct mips_frame_info *info)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
void *func = info->func;
|
void *func = info->func;
|
||||||
|
@ -329,14 +329,12 @@ static int __init get_frame_info(struct mips_frame_info *info)
|
||||||
ip->i_format.simmediate / sizeof(long);
|
ip->i_format.simmediate / sizeof(long);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info->pc_offset == -1 || info->frame_size == 0) {
|
if (info->frame_size && info->pc_offset >= 0) /* nested */
|
||||||
if (func == schedule)
|
|
||||||
printk("Can't analyze prologue code at %p\n", func);
|
|
||||||
info->pc_offset = -1;
|
|
||||||
info->frame_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
if (info->pc_offset < 0) /* leaf */
|
||||||
|
return 1;
|
||||||
|
/* prologue seems boggus... */
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init frame_info_init(void)
|
static int __init frame_info_init(void)
|
||||||
|
@ -367,8 +365,15 @@ static int __init frame_info_init(void)
|
||||||
mfinfo[0].func = schedule;
|
mfinfo[0].func = schedule;
|
||||||
schedule_frame = &mfinfo[0];
|
schedule_frame = &mfinfo[0];
|
||||||
#endif
|
#endif
|
||||||
for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++)
|
for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) {
|
||||||
get_frame_info(&mfinfo[i]);
|
struct mips_frame_info *info = &mfinfo[i];
|
||||||
|
if (get_frame_info(info)) {
|
||||||
|
/* leaf or unknown */
|
||||||
|
if (info->func == schedule)
|
||||||
|
printk("Can't analyze prologue code at %p\n",
|
||||||
|
info->func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mfinfo_num = i;
|
mfinfo_num = i;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -427,6 +432,8 @@ unsigned long get_wchan(struct task_struct *p)
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (mfinfo[i].pc_offset < 0)
|
||||||
|
break;
|
||||||
pc = ((unsigned long *)frame)[mfinfo[i].pc_offset];
|
pc = ((unsigned long *)frame)[mfinfo[i].pc_offset];
|
||||||
if (!mfinfo[i].frame_size)
|
if (!mfinfo[i].frame_size)
|
||||||
break;
|
break;
|
||||||
|
@ -437,3 +444,40 @@ unsigned long get_wchan(struct task_struct *p)
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KALLSYMS
|
||||||
|
/* used by show_frametrace() */
|
||||||
|
unsigned long unwind_stack(struct task_struct *task,
|
||||||
|
unsigned long **sp, unsigned long pc)
|
||||||
|
{
|
||||||
|
unsigned long stack_page;
|
||||||
|
struct mips_frame_info info;
|
||||||
|
char *modname;
|
||||||
|
char namebuf[KSYM_NAME_LEN + 1];
|
||||||
|
unsigned long size, ofs;
|
||||||
|
|
||||||
|
stack_page = (unsigned long)task_stack_page(task);
|
||||||
|
if (!stack_page)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf))
|
||||||
|
return 0;
|
||||||
|
if (ofs == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
info.func = (void *)(pc - ofs);
|
||||||
|
info.func_size = ofs; /* analyze from start to ofs */
|
||||||
|
if (get_frame_info(&info)) {
|
||||||
|
/* leaf or unknown */
|
||||||
|
*sp += info.frame_size / sizeof(long);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((unsigned long)*sp < stack_page ||
|
||||||
|
(unsigned long)*sp + info.frame_size / sizeof(long) >
|
||||||
|
stack_page + THREAD_SIZE - 32)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pc = (*sp)[info.pc_offset];
|
||||||
|
*sp += info.frame_size / sizeof(long);
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -98,24 +98,53 @@ static void show_trace(unsigned long *stack)
|
||||||
printk("\n");
|
printk("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KALLSYMS
|
||||||
|
static int raw_show_trace;
|
||||||
|
static int __init set_raw_show_trace(char *str)
|
||||||
|
{
|
||||||
|
raw_show_trace = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
__setup("raw_show_trace", set_raw_show_trace);
|
||||||
|
|
||||||
|
extern unsigned long unwind_stack(struct task_struct *task,
|
||||||
|
unsigned long **sp, unsigned long pc);
|
||||||
|
static void show_frametrace(struct task_struct *task, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
const int field = 2 * sizeof(unsigned long);
|
||||||
|
unsigned long *stack = (long *)regs->regs[29];
|
||||||
|
unsigned long pc = regs->cp0_epc;
|
||||||
|
int top = 1;
|
||||||
|
|
||||||
|
if (raw_show_trace || !__kernel_text_address(pc)) {
|
||||||
|
show_trace(stack);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printk("Call Trace:\n");
|
||||||
|
while (__kernel_text_address(pc)) {
|
||||||
|
printk(" [<%0*lx>] ", field, pc);
|
||||||
|
print_symbol("%s\n", pc);
|
||||||
|
pc = unwind_stack(task, &stack, pc);
|
||||||
|
if (top && pc == 0)
|
||||||
|
pc = regs->regs[31]; /* leaf? */
|
||||||
|
top = 0;
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define show_frametrace(task, r) show_trace((long *)(r)->regs[29]);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine abuses get_user()/put_user() to reference pointers
|
* This routine abuses get_user()/put_user() to reference pointers
|
||||||
* with at least a bit of error checking ...
|
* with at least a bit of error checking ...
|
||||||
*/
|
*/
|
||||||
void show_stack(struct task_struct *task, unsigned long *sp)
|
static void show_stacktrace(struct task_struct *task, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
const int field = 2 * sizeof(unsigned long);
|
const int field = 2 * sizeof(unsigned long);
|
||||||
long stackdata;
|
long stackdata;
|
||||||
int i;
|
int i;
|
||||||
unsigned long *stack;
|
unsigned long *sp = (unsigned long *)regs->regs[29];
|
||||||
|
|
||||||
if (!sp) {
|
|
||||||
if (task && task != current)
|
|
||||||
sp = (unsigned long *) task->thread.reg29;
|
|
||||||
else
|
|
||||||
sp = (unsigned long *) &sp;
|
|
||||||
}
|
|
||||||
stack = sp;
|
|
||||||
|
|
||||||
printk("Stack :");
|
printk("Stack :");
|
||||||
i = 0;
|
i = 0;
|
||||||
|
@ -136,7 +165,44 @@ void show_stack(struct task_struct *task, unsigned long *sp)
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
printk("\n");
|
printk("\n");
|
||||||
show_trace(stack);
|
show_frametrace(task, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static noinline void prepare_frametrace(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"1: la $2, 1b\n\t"
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
"sd $2, %0\n\t"
|
||||||
|
"sd $29, %1\n\t"
|
||||||
|
"sd $31, %2\n\t"
|
||||||
|
#else
|
||||||
|
"sw $2, %0\n\t"
|
||||||
|
"sw $29, %1\n\t"
|
||||||
|
"sw $31, %2\n\t"
|
||||||
|
#endif
|
||||||
|
: "=m" (regs->cp0_epc),
|
||||||
|
"=m" (regs->regs[29]), "=m" (regs->regs[31])
|
||||||
|
: : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_stack(struct task_struct *task, unsigned long *sp)
|
||||||
|
{
|
||||||
|
struct pt_regs regs;
|
||||||
|
if (sp) {
|
||||||
|
regs.regs[29] = (unsigned long)sp;
|
||||||
|
regs.regs[31] = 0;
|
||||||
|
regs.cp0_epc = 0;
|
||||||
|
} else {
|
||||||
|
if (task && task != current) {
|
||||||
|
regs.regs[29] = task->thread.reg29;
|
||||||
|
regs.regs[31] = 0;
|
||||||
|
regs.cp0_epc = task->thread.reg31;
|
||||||
|
} else {
|
||||||
|
prepare_frametrace(®s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
show_stacktrace(task, ®s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -146,6 +212,14 @@ void dump_stack(void)
|
||||||
{
|
{
|
||||||
unsigned long stack;
|
unsigned long stack;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KALLSYMS
|
||||||
|
if (!raw_show_trace) {
|
||||||
|
struct pt_regs regs;
|
||||||
|
prepare_frametrace(®s);
|
||||||
|
show_frametrace(current, ®s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
show_trace(&stack);
|
show_trace(&stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +339,7 @@ void show_registers(struct pt_regs *regs)
|
||||||
print_modules();
|
print_modules();
|
||||||
printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n",
|
printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n",
|
||||||
current->comm, current->pid, current_thread_info(), current);
|
current->comm, current->pid, current_thread_info(), current);
|
||||||
show_stack(current, (long *) regs->regs[29]);
|
show_stacktrace(current, regs);
|
||||||
show_code((unsigned int *) regs->cp0_epc);
|
show_code((unsigned int *) regs->cp0_epc);
|
||||||
printk("\n");
|
printk("\n");
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче